Мы уже привыкли пользоваться setTimeout(fn, 0)
, чтобы запланировать вызов некой функции и продолжить выполнение текущего кода. Мы ожидаем, что запланированный вызов произойдёт максимально быстро на сколько это возможно. Однако у всех JavaScript движков есть ограничение — 4 миллисекунды. Побороть это ограничение можно, если пользоваться
requestAnimationFrame
или setImmediate
.
/**
* Runs the callback at the next available opportunity.
* @see https://developer.mozilla.org/en-US/docs/Web/API/window.setImmediate
*/
var setImmediate = function(callback) {
return (
window.setImmediate ||
window.requestAnimationFrame ||
window.mozRequestAnimationFrame ||
window.webkitRequestAnimationFrame ||
window.setTimeout
)(callback, 0);
};
/**
* Clears a callback previously registered with `setImmediate`.
* @param {id} id The identifier of the callback to abort
*/
var clearImmediate = function(id) {
return (window.clearImmediate ||
window.cancelAnimationFrame ||
window.webkitCancelAnimationFrame ||
window.mozCancelAnimationFrame ||
window.clearTimeout)(id);
};
Метод setImmediate
объекта window
был реализован в IE10 и Node.js 10+, но встретил сопротивление со стороны разработчиков Firefox и WebKit из-за неясности спецификации.
Коментарии к заметке
Разве requestAnimationFrame «тикает» не раз в 16 мс в лучшем случае?
Нет, Давид. Там всё очень сложно.
Запусти такой пример http://jsfiddle.net/mistakster/GRTas/ и посмотри в консоль. Это простой тест для определения длительности задержки с помощью Performance API. Его можно запустить с разным количеством одновременно выполняемых функций.
Удивительно, но при достаточно больших количествах
requestAnimationFrame
безоговорочно выдаёт меньшую задержку во всех браузерах. Тогда как при маленьких значениях нет абсолютно никакой закономерности в задержке. Похоже всё зависит от того как синхронизированы «вертикальная развёртка» и главный цикл JS.Все таки requestAnimationFrame и setTimeout не совсем то же самое, что setImmediate — у них очень непредсказуемый интервал, который зависит от многих факторов. Есть более неплохой pollyfil, который использует разные техники и позволяет добиться более раннего срабатывания заданной функции в большинстве случаев: https://github.com/YuzuJS/setImmediate Собственно это решение является вдохновителем для других подобных решений, например в basis.js https://github.com/basisjs/basisjs/blob/master/src/basis.js#L585-L766
Роман, спасибо большое за ссылочки.