Skip to main content

setTimeout(fn, 0)

setTimeout(fn, 0) не виконує функцію миттєво: він просто ставить колбек fn у чергу макрозадач, щоб той виконався після завершення поточного коду і всіх мікрозадач. Нульова затримка означає «у наступній ітерації Event Loop», а не «зараз».

Теорія

TL;DR

  • setTimeout(fn, 0) ставить fn у чергу макрозадач, а не виконує його одразу.
  • Колбек чекає, доки звільниться стек викликів, бо Event Loop ніколи не перериває код, що вже виконується.
  • Перед ним встигнуть виконатися всі мікрозадачі: Promise.then, queueMicrotask.
  • Порядок завжди такий: стек викликів, потім мікрозадачі, потім setTimeout.
  • Це стандартний спосіб відкласти роботу, розірвати довгий синхронний блок і дати інтерфейсу оновитися.
  • Виклик повертає ID таймера, який можна скасувати через clearTimeout.

Швидкий приклад

javascript
console.log('A'); setTimeout(() => console.log('B'), 0); console.log('C');

Вивід:

text
A C B

Чому не A, потім B, потім C? Бо колбек таймера не має права втрутитися в код, який уже виконується.

Що відбувається крок за кроком

  1. console.log('A') виконується одразу, у стеку викликів (Call Stack).
  2. setTimeout(() => console.log('B'), 0) передає задачу у Web API, це таймер із затримкою 0 мс.
  3. Таймер миттєво «спрацьовує», і колбек додається у чергу макрозадач.
  4. JavaScript продовжує виконувати поточний стек, тобто друкує C.
  5. Коли стек викликів порожній, Event Loop бере задачу () => console.log('B') з черги і виконує її.

Чому навіть 0 не виконується одразу

Тому що в JavaScript усе проходить через Event Loop, а він ніколи не перериває поточний стек викликів.

Навіть якщо затримка дорівнює 0, колбек все одно мусить дочекатися, поки стек звільниться.

Тобто setTimeout(fn, 0) це спосіб відкласти виконання функції до наступної ітерації Event Loop.

Варто пам'ятати ще одну деталь: за специфікацією браузери додають мінімальний поріг приблизно 4 мс для вкладених таймерів, тому реальна затримка майже ніколи не дорівнює нулю.

Приклад із промісом (мікрозадача)

javascript
setTimeout(() => console.log('timeout'), 0); Promise.resolve().then(() => console.log('promise')); console.log('end');

Порядок:

text
end promise timeout

Пояснення:

  • Promise.then() створює мікрозадачу;
  • setTimeout() створює макрозадачу;
  • а мікрозадачі завжди виконуються раніше за макрозадачі.

Практичне застосування

  1. Відкласти виконання, щоб не блокувати основний код:

    javascript
    setTimeout(() => { console.log('Виконається після основного коду'); }, 0);
  2. Розірвати синхронний цикл, щоб інтерфейс встиг оновитися:

    javascript
    for (let i = 0; i < 1e9; i++) {} // важка операція console.log('Інтерфейс підвис...'); setTimeout(() => { console.log('Тепер виконуємося після паузи'); }, 0);
  3. Скинути стек, наприклад у рекурсивних обчисленнях:

    javascript
    function nextStep() { setTimeout(nextStep, 0); // кожне повторення це нова ітерація Event Loop } nextStep();

Підсумок

ПараметрЗначення
Що робитьСтавить колбек у чергу макрозадач
Коли виконуєтьсяПісля поточного стека і всіх мікрозадач
Затримка «0»Не миттєво, а в наступному циклі Event Loop
ЧергаМакрозадачі
ПовертаєID таймера, який можна скасувати через clearTimeout

Візуально це виглядає так:

text
[Call Stack] -> виконується поточний код [Microtask Queue] -> проміси і queueMicrotask [Task Queue] -> setTimeout(fn, 0)

setTimeout(fn, 0) завжди чекає, поки:

  1. очиститься стек викликів,
  2. виконаються мікрозадачі,
  3. і лише потім виконується fn.

Типові помилки

  • Думати, що 0 означає «негайно». Це означає «якнайшвидше після звільнення стека», різниця критична, коли поруч є важкий синхронний код.
  • Ставити setTimeout(fn, 0) в один ряд із Promise.resolve().then(fn). Проміс це мікрозадача, вона завжди випередить таймер.
  • Розраховувати на рівно 0 мс. Браузери застосовують мінімальний поріг близько 4 мс для вкладених таймерів, а у фоновій вкладці затримки ще більші.
  • Лікувати нульовим таймером зависання інтерфейсу. Він лише дає браузеру шанс перемалювати екран між шматками роботи; якщо сама робота важка, її треба розбити на частини або винести у Web Worker.
  • Забувати про clearTimeout. Відкладений колбек може спрацювати вже після того, як компонент знято з екрана, і звернутися до неактуальних даних.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.