Предпочтительный способ опроса локального значения в Javascript?

Например, существует переменная с именем animationComplete (из сторонней библиотеки) и функция с именем happenAfterAnimation:

Простое решение выглядит так:

while(!animationComplete) {
   // Do nothing
}
happenAfterAnimation()

Или более сложное решение, подобное этому:

function tryHappenAfterAnimation() {
  if(animationComplete) {
    happenAfterAnimation()
  } else {
    setTimeout(tryHappenAfterAnimation, 100)
  }

}
setTimeout(tryHappenAfterAnimation, 100)

Первое решение может иметь некоторые накладные расходы, а второе решение выглядит немного грязным.

Поскольку future/promise недоступен в текущей версии Javascript, здесь может быть немного перебор. Мне просто интересно, есть ли элегантный и легкий способ для этой ситуации.

Есть ли у кого-нибудь идеи о лучшем способе справиться с этим? Спасибо!

Ответ 1

Первый подход не будет работать вообще. Он будет блокировать поток навсегда.

Отсутствие встроенной поддержки обещаний на самом деле не является оправданием, чтобы не использовать promises. Если ваша библиотека дает вам способ для события/обратного вызова, чтобы ждать результатов (и я был бы удивлен, если бы это не так), то вы должны использовать событие или promises.

Если нет, и опрос переменной - это ваш единственный вариант, я бы сказал, что ваш второй подход - это почти единственный вариант.

Ответ 2

Краткая сводка. Ваш первый метод не будет работать (он будет просто бесконечным циклом), и механизм опроса (пока он может работать) никогда не является "предпочтительным" выбором.

Javascript является однопоточным (с некоторыми исключениями, такими как webWorkers, но они здесь не применяются), так как пока цикл while работает, никакая другая JS не может запускаться, что может изменить значение animationComplete, чтобы вы могли имеют бесконечный цикл, который никогда не будет завершен (в конце концов браузер будет жаловаться на то, что поток JS не завершился, и предложит пользователю возможность остановить script).

Правильный способ узнать, когда завершена анимация, - использовать обратный вызов для завершения анимации. Если вы используете jQuery, то все анимации jQuery имеют обратный вызов завершения, который вы можете передать, и он будет вызван, когда анимация будет выполнена. Затем вы помещаете код, который должен произойти после анимации в этой функции обратного вызова.

Если вы используете анимацию CSS3, вы можете зарегистрировать обработчик событий для события transitionend и получить обратный вызов таким образом, когда анимация будет завершена.

Если вы хотите опросить значение переменной, то использование таймера для опроса будет подходящим выбором, так как это позволяет запускать другой код, который может фактически изменить переменную.

Я должен добавить, что подобный опрос редко является лучшим выбором дизайна. Если это анимация, то вы просто хотите использовать событие завершения анимации, поэтому опрос не требуется. Если по какой-то причине вы используете функцию анимации, которая не имеет встроенного события завершения, то, вероятно, стоит изменить этот код, чтобы включить такой обратный вызов, который всегда будет возможен и, очевидно, полезен.


Чтобы предоставить более подробную рекомендацию, нам нужно будет узнать больше о коде анимации и о том, что вы пытаетесь сделать. Как я уже говорил, все анимации jQuery обеспечивают обе возможности и поддержку обратного вызова для уведомления о завершении (вы отметили вопрос jQuery, почему я упоминаю об этом).

Отсутствие promises в любом интерфейсе не является основанием для того, чтобы не использовать обратные вызовы завершения, так что факт здесь не имеет особого значения, и любая заданная функция может быть пролонгирована тоже (превратите обычный обратный вызов в интерфейс обещания), если promises - желаемый способ взаимодействия с асинхронными событиями.

Ответ 3

Вот подход, основанный на Promises. Следующее выполняет периодический опрос вместе с таймаутом.

var Promise = require('bluebird');

/**
 * Periodically poll a signal function until either it returns true or a timeout is reached.
 *
 * @param signal function that returns true when the polled operation is complete
 * @param interval time interval between polls in milliseconds
 * @param timeout period of time before giving up on polling
 * @returns true if the signal function returned true, false if the operation timed out
 */
function poll(signal, interval, timeout) {
    function pollRecursive() {
        return signal() ? Promise.resolve(true) : Promise.delay(interval).then(pollRecursive);
    }

    return pollRecursive()
        .cancellable()
        .timeout(timeout)
        .catch(Promise.TimeoutError, Promise.CancellationError,function () {
            return false;
        });
}

Вы называете это так.

poll(animationComplete, pollingInterval, timeout)
    .then(function(complete) {
        if (complete)
            happenAfterAnimation();
    }
);

Смотрите Опрос Javascript с promises.