JavaScript и кое-что ещё: 4 креативных подхода к измерению времени в браузерах

Моя цель - предложение широкого ассортимента товаров и услуг на постоянно высоком качестве обслуживания по самым выгодным ценам.

Прежде чем перейти к статье, хочу вам представить, экономическую онлайн игру Brave Knights, в которой вы можете играть и зарабатывать. Регистируйтесь, играйте и зарабатывайте!

Автор статьи, перевод которой мы сегодня публикуем, решил рассказать о нескольких необычных способах измерения времени в браузерах. Для их использования понадобится доступ к различным API, которые применяются в веб-разработке, поэтому они не подходят для платформы Node.js. Правда, если кто-то нуждается в необычном способе измерения времени в Node.js, то, полагаем, после прочтения этого материала у него могут появиться кое-какие идеи на этот счёт.



Использование бесконечного синхронного цикла в веб-воркере (не в сервис-воркере)


Так как веб-воркеры, в сущности, представляют собой потоки, работающие в веб-браузере, в них можно запускать бесконечные циклы, не рискуя заблокировать главный поток. Это позволяет работать с отрезками времени, длительность которых составляет менее миллисекунды. Такая точность особенно хорошо подходит для тех случаев, когда в воркере нужно принимать решения, которые сильно зависят от времени. О принятии этих решений можно (с очень высоким уровнем точности) сообщать в главный поток. Например, можно что-то выводить в том случае, когда количество прошедших с некоего события микросекунд представлено простым числом. Для того чтобы работать со временем с микросекундной точностью, можно воспользоваться методом performance.now.

▍Достоинства


  1. Достижима точность, выражаемая микросекундами.
  2. На главный поток не ложится практически никакой нагрузки.
  3. Полностью асинхронный, с точки зрения главного потока, механизм измерения времени. Это возможно благодаря особенностям устройства системы обмена сообщениями между потоком веб-воркера и главным потоком.
  4. Безопасное завершение работы. В отличие от неопределённости, сопряжённой с использованием setInterval, вызов метода воркера terminate гарантирует то, что после завершения работы воркера от него больше не будут поступать сообщения со сведениями о времени. В MDN по этому поводу говорится следующее: «Метод terminate() интерфейса Worker немедленно завершает работу воркера. Ему не дается возможность завершить свою работу, он останавливается сразу».

▍Недостатки


  1. Даже хотя этот метод даёт возможность работать с интервалами времени, которые меньше миллисекунды, отправка сообщений в главный поток работает асинхронно. То есть, нельзя произвести некие действия в главном потоке с той же точностью, с которой в воркере принимается решение о том, что эти действия нужно произвести.
  2. Этот метод полностью загружает поток, что может привести к повышенному расходу заряда аккумуляторов на телефонах.
  3. Для работы этого метода нужна поддержка веб-воркеров.
  4. Бесконечный цикл в веб-воркере не приостанавливается в том случае, если вкладка, связанная с ним, неактивна.

→ Пример на Codesandbox

Использование CSS-анимаций ради событий, связанных со временем (в частности — animationiteration)


Этот метод подразумевает создание некоего элемента с бесконечной анимацией. Тут можно попробовать воспользоваться элементом div, но надо сказать, что, как отмечено во 2 пункте перечня недостатков этого метода, элементами div для этих целей лучше не пользоваться. Итак, если у нас имеется элемент с бесконечной анимацией, мы можем подписаться на его событие animationiteration и получать уведомления в те моменты, когда будет истекать интервал animation-duration.

▍Достоинства


  1. Автоматическая приостановка в том случае, если браузерная вкладка становится неактивной. В таком случае интересующее нас событие просто не происходит. Поэтому не нужно заботиться о решении проблемы обработки множества событий, накопившихся за время, когда вкладка была неактивной, а потом была активирована.
  2. Автоматическое освобождение ресурсов при удалении скрытого элемента div из DOM. Например, если имеется React-компонент, который выводит время, то ничего особенного при его отмонтировании делать не нужно. Элемент div будет удалён и событие больше вызываться не будет.
  3. Этот плюс данного метода субъективен, но надо отметить, что логика подписки выглядит просто прекрасно. Например — так:

    .addEventListener("animationiteration", fun).
  4. Чистейший способ откладывания запуска таймера с использованием animation-delay.

▍Недостатки


  1. Этот метод не отличается интуитивной понятностью. Его использование может запутать других программистов, работающих над тем проектом, где он применяется.
  2. Зависимость от DOM и CSSOM. Другие CSS-правила могут повлиять на те, которые описывают анимацию. Поэтому я советую создать для целей организация таймера некий произвольно названный ранее несуществующий тег вроде <just-a-timer-element></<just-a-timer-element>. Возможно — стоит создать пользовательский элемент, внутри которого аккуратно спрятан код CSS-анимации? (всё это — довольно спорные идеи, на самом деле).
  3. Этот метод не работает в том случае, если у элемента есть стиль display: none;.
  4. Неточность. В соответствии с проведёнными мной испытаниями, точность отсчёта времени может быть около 1 мс. Вы, чтобы выяснить, как именно это работает у вас, можете поэкспериментировать с примером, ссылка на который приведена ниже.

→ Пример на Codesandbox

Использование тега svg (SMIL-анимации)


Взгляните на следующий SVG-элемент:

<svg>
  <rect>
    <animate
      attributeName="rx"
      values="0;1"
      dur="1s"
      repeatCount="indefinite"
    />
  </rect>
</svg>

Если воспользоваться следующим кодом: animate.addEventListener('repeat', fun), то функция fun будет вызываться каждые dur секунд. В нашем случае — каждую секунду.

▍Достоинства


  1. Этот метод работает даже в том случае, если SVG-элементу назначен стиль display: none;.
  2. Отсчёт времени автоматически останавливается тогда, когда SVG-элемент удаляется из DOM.
  3. Генерирование событий не начинается до полной загрузки страницы.
  4. «Таймер» автоматически приостанавливается в том случае, если вкладка становится неактивной.

▍Недостатки


  1. Как и в случае с отсчётом временных интервалов с использованием CSS-анимации, применение этого метода может показаться непонятным другим программистам.
  2. Зависимость от DOM и CSSOM. То есть — тут мы имеем те же нежелательные особенности, что уже были описаны для метода отсчёта времени с помощью CSS-анимации. А именно — возможность нарушения работы «таймера» из-за других CSS-правил.
  3. Не поддерживается в IE и Edge (до перевода браузера от Microsoft на Chromium).
  4. Неточность. В соответствии с моими испытаниями, отклонения такого таймера от самого точного метода могут составлять весьма внушительные 15 миллисекунд. Можете сами это проверить, поэкспериментировав с примером, ссылка на который дана ниже.
  5. Отсчёт времени не начинается до полной загрузки страницы. Правда, этот «минус» данного метода может в какой-нибудь ситуации оказаться и «плюсом».

→ Пример на Codesandbox

Использование Web Animations API


Web Animations API позволяет анимировать DOM-элементы средствами JavaScript-кода.

Интересно то, что можно анимировать даже отмонтированные элементы! Это даёт доступ к механизмам работы со временем, доступным в чистом JavaScript (и в Web API).

Вот альтернативная реализация setTimeout:

function ownSetTimeout(callback, duration) {
  const div = document.createElement('div');

  const keyframes = new KeyframeEffect(div, [], { duration, iterations: 1 });

  const animation = new Animation(
    keyframes,
    document.timeline
  );

  animation.play();

  animation.addEventListener('finish', () => {
    callback();
  });
}

Аккуратно, правда?

▍Достоинства


  1. Самодостаточное решение, не требующее взаимодействия с DOM.
  2. Незнакомый с этим подходом программист легко поймёт смысл соответствующего кода.
  3. «Таймер» приостанавливается на неактивной вкладке.

▍Недостатки


  1. Web Animations API — технология всё ещё экспериментальная. Не стоит пользоваться ей в продакшне.
  2. Очень плохая браузерная поддержка. Этот метод, вероятно, будет работать лишь в Chromium.
  3. При всех плюсах этого решения, оно, всё же, может показаться кому-то далёким от интуитивной понятности.
  4. То, что «таймер» приостанавливается на неактивной вкладке, может оказаться минусом этого решения в том случае, если оно используется как альтернатива setTimeout.
  5. Этот метод нельзя использовать для отсчёта интервалов. Программисту доступно лишь событие onfinish.
  6. Невысокая точность. В соответствии с моими экспериментами, ошибка легко может составлять ±5 миллисекунд.

→ Пример на Codesandbox

Бонус


Для того чтобы работать со временем, можно воспользоваться Web Audio API. Это — ещё один замечательный способ точной работы с интервалами и задержками. Вот отличная статья об этом.

Итоги


Я понимаю, что методики работы со временем, которые я тут перечислил, способны принести пользу далеко не всем. Но я просто не мог не написать эту статью, так как всегда думал, что setTimeout и setInterval — это единственные способы для асинхронной работы с некими отрезками времени. А на самом деле, как оказалось, это — далеко не всё. Кто знает, возможно, кому-то придётся столкнуться с какими-то необычными ограничениями при работе над неким проектом, с какими-то особыми условиями, в которых освещённые здесь методы работы со временем могут оказаться полезными.

Уважаемые читатели! Как вы думаете, в каких ситуациях могут пригодиться описанные здесь подходы работы со временем?

Источник: https://habr.com/ru/company/ruvds/blog/489818/


Интересные статьи

Интересные статьи

В своей прошлой статье я прикидывал, какие namespace'ы мне нужны для упорядочивания кода в ES6-модулях. В этой статье я описываю, какие namespace'ы у меня получились и ка...
Шел очередной день самоизоляции, и я делал один из тех проектов для себя, которые мы забрасываем через пару дней после того как начали. Ну вы знаете, тот проект, который сделает вас з...
Недавно мне на глаза попалась одна статья на Хабре. В ней сравниваются C# и JavaScript. На мой взгляд, сравнивать их — всё равно что сравнивать луну и солнце, которые, если верить...
Здравствуй, Хабр! Данное руководство является первой частью в запланированном цикле статей про такой замечательный фреймворк для тестирования как Jest. Материал будет полезен новичкам...
Автокэширование в 1с-Битрикс — хорошо развитая и довольно сложная система, позволяющая в разы уменьшить число обращений к базе данных и ускорить выполнение страниц.