Гарантія виконання таймера
Короткa відповідь
Ні, не можна гарантувати точний час виконання таймера
у JavaScript (setTimeout, setInterval і т. п.).
Причина проста: JavaScript однопотоковий, і виконання таймера залежить від стану Event Loop та завантаженості стека викликів.
Чому так відбувається
Коли ти викликаєш:
setTimeout(fn, 1000);JS не обіцяє, що fn виконається через рівно 1000 мс.
Він лише ставить завдання в чергу, щоб виконати її не раніше, ніж через 1000 мс.
Якщо в момент, коли таймер "спрацював", стек викликів зайнятий, колбек чекатиме своєї черги.
Приклад
console.log('Старт');
setTimeout(() => console.log('Таймер 1s'), 1000);
const start = Date.now();
while (Date.now() - start < 3000) {} // блокуємо потік 3 секунди
console.log('Кінець');Результат:
Старт
(3 секунди паузи)
Кінець
Таймер 1sХоча таймер був на 1 секунду, спрацював він лише через 3 секунди. Чому? Тому що рушій не міг виконати його раніше - стек був зайнятий циклом.
Мінімальна затримка (throttling)
Навіть setTimeout(fn, 0) не запускає код "одразу" - мінімальна затримка все одно є:
- У браузерах: зазвичай 4 мс (за стандартом HTML Living Standard),
- У Node.js: може бути менше, але теж не миттєво.
Крім того, якщо вкладка браузера неактивна, затримка може бути збільшена (до 1000 мс і більше) - браузери економлять ресурси.
Приклад з setInterval
setInterval() теж не гарантує точні інтервали.
Якщо функція всередині виконується довше, ніж сам інтервал, виклики будуть "накопичуватись" і виконуватись із запізненням.
setInterval(() => {
const start = Date.now();
while (Date.now() - start < 1500) {} // блокуємо 1.5 сек
console.log('tick');
}, 1000);Інтервал - 1 секунда, але функція займає 1.5 сек, тому "тіки" йдуть з реальною затримкою 1.5, 3.0, 4.5... сек.
Чому не можна точно
| Причина | Пояснення |
|---|---|
| Однопотоковість JS | Поки виконується код, інші завдання чекають |
| Event Loop | Таймери лише потрапляють у чергу - не запускаються миттєво |
| Обмеження браузера | Є мінімальна затримка, а у фоні - сповільнення |
| Навантаження CPU | При високому навантаженні таймери виконуються пізніше |
| Планувальник ОС | JS не керує системним часом, а лише "просить" запустити завдання пізніше |
Що можна зробити
Якщо тобі потрібна максимальна точність (наприклад, в анімації чи синхронізації), можна використовувати альтернативи:
performance.now()- вимірює точний час у мілісекундах (з мікросекундами):
const start = performance.now();
// ...
const elapsed = performance.now() - start;requestAnimationFrame()- запускає колбек перед кожним кадром (~60 разів на секунду), ідеально підходить для плавних анімацій.setTimeoutз компенсацією дрейфу (корекція затримки):
const start = Date.now();
let count = 0;
function tick() {
count++;
const expected = start + count * 1000;
const drift = Date.now() - expected;
console.log(`tick ${count}, drift: ${drift}ms`);
setTimeout(tick, 1000 - drift);
}
tick();Такий підхід враховує "запізнення" і вирівнює інтервали.
Підсумок
| Питання | Відповідь |
|---|---|
| Чи можна гарантувати точний час таймера? | Ні |
| Чому? | Однопотоковість JS, Event Loop, мінімальні затримки та навантаження |
| Що реально відбувається? | Таймер додається в чергу завдань, виконується пізніше |
| Чи є альтернатива для точності? | requestAnimationFrame, performance.now, компенсація дрейфу |
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.