Вопрос проверяет понимание того, что таймеры в браузере работают как “минимальная задержка”, а не точный планировщик.
setTimeout не гарантирует выполнение callback ровно через указанное время. Он гарантирует, что callback не выполнится раньше, чем пройдёт заданная задержка. После истечения задержки callback попадает в очередь задач и будет выполнен, когда освободится call stack. На фактическое время влияет нагрузка, блокировки и приоритеты очередей. Поэтому таймер часто срабатывает позже.
setTimeout(fn, ms) — это таймер с минимальной задержкой, а не “точный будильник”.
Браузер запускает таймер на ms
Когда время прошло, callback не запускается сразу
Callback ставится в очередь macrotask
Он выполнится только тогда, когда:
call stack пуст
очередь microtask очищена
Event Loop доберётся до этой задачи
Причины задержек:
в call stack выполняется длинный синхронный код
много microtask (например, цепочки Promise.then)
браузер занят рендером/обработкой событий
вкладка в фоне (браузеры могут троттлить таймеры)
setTimeout(() => console.log('timeout'), 0)
const start = Date.now()
while (Date.now() - start < 1000) {
// блокируем поток на 1 секунду
}
Даже при 0 callback выполнится после того, как завершится цикл while.
setTimeout(..., 0) — это не “сразу”, а “как получится, когда очередь дойдёт”
для точных интервалов (например, анимаций) обычно используют requestAnimationFrame
для “периодических задач” с контролем drift — часто пишут собственный планировщик
Вывод:setTimeout гарантирует только минимальную задержку, а фактическое выполнение зависит от очередей и занятости основного потока.