Тонкости реализации requestAnimationFrame

Те, кому приходилось плотно заниматься анимацией в JS наверняка сталкивались с rAF. Эта функция помогает синхронизировать анимацию с обновлением страницы, которое в свою очередь синхронизируется с обновлением экрана. Это позволяет добиться максимально плавной анимации без рывков.

(function step() {
    render();
    requestAnimationFrame(step);
})();

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

В первую очередь пришла мысль прикинуть, а как бы я реализовал такое API. Очевидно, что выполнять переданную функцию прямо перед отрисовкой не вариант, так как мы точно не знаем, сколько выполняется данная функция, и вообще это время может меняться от различных условий. Самым правильным вариантом было бы запускать выполнение функции сразу после отрисовки страницы. В этом случае у функции будет максимальное время, чтобы обновить состояние страницы. Но даже если всё происходит именно так, остается два важных вопроса:

Когда первый раз выполняется функция переданная в rTF? Каким образом обрабатывается несколько вызовов rAF подряд? Аргумент принимаемый в выполняемую функцию является timestamp’ом, какую именно информацию он несёт? Чтобы это выяснить я написал небольшой фрагмент кода, содержащий вызовы rTF, который логировал время вызовов. В итоге мне удалось не только подтвердить свою теорию, но и получить ответы на все вопросы описанные выше:

В первый раз переданная функция выполняется максимально быстро, как если бы мы использовали setTimeout(..., 0); Если вызвать несколько раз подряд rAF, то переданные функции будут вызваны последовательно без задержки, т.е. каждый rAF работает независимо от других. timestamp приходящий в переданную функцию является временем в мс (Float с большой точностью) от начала загрузки страницы до времени последней отрисовки страницы. Если было несколько вызовов rAF, то соответственно этот timestamp для разных переданных функций будет одинаковым для одного кадра!
Хочу сразу уточнить, что полученные результаты являются лишь результатом анализа экспериментальных данных и не являются истиной в последней инстанции. Вполне может быть, что какие-то принципы работы rAF были поняты мной неправильно. Буду рад дополнениям и замечаниям.

Надеюсь для кого-то эта информация будет полезной ;)

Дополнительные сведения о rAF можно почерпнуть здесь