Тайминги в веб-приложениях: почему 19 секунд ≠ 19 секунд?

Хочу поделиться наблюдением, с которым столкнулась при разработке дыхательного виджета. Делала простой, казалось бы, инструмент для дыхательных практик — те самые популярные режимы вроде 4-7-8 (вдох на 4 секунды, задержка на 7, выдох на 8). Всё просто: кружок растёт-держится-уменьшается, счётчик тикает.

Но обнаружила интересную особенность: при общем времени в 19 секунд console.timeEnd показывал 18998 миллисекунд, хотя визуально анимация завершилась ровно в 19 секунд. Откуда взялась разница в 2 миллисекунды?

Оказалось, console.timeEnd показал меньшее значение потому, что он фиксирует момент, когда JavaScript код завершил выполнение логики анимации. Но в этот момент результат ещё не отрисован на экране. Браузеру требуется дополнительное время, чтобы обновить интерфейс — обычно это несколько миллисекунд, пока система готовит кадр для отображения. При этом само выполнение кода заняло чуть меньше времени из-за особенностей Event Loop. Когда задачи попадают в очередь и извлекаются оттуда, могут возникать микроскопические расхождения во времени. Браузер старается оптимизировать выполнение, и иногда операции занимают чуть меньше времени, чем ожидалось.

В итоге получилось, что где-то время сэкономилось на выполнении кода, а где-то добавилось на отрисовку.