Запропонувати правкуПокращити цю статтюДопрацюйте відповідь до «setTimeout(fn, 0)». Ваші зміни проходять модерацію перед публікацією.Потрібне підтвердженняКонтентЩо ви змінюєте🇺🇸EN🇺🇦UAПереглядЗаголовок (UA)Коротка відповідь (UA)**`setTimeout(fn, 0)` не виконує функцію миттєво: він лише ставить колбек `fn` у чергу макрозадач, щоб той виконався після завершення поточного коду і всіх мікрозадач.** Затримка `0` означає «якнайшвидше після звільнення стека викликів», а не «негайно», адже Event Loop ніколи не перериває поточний стек. Тому такий виклик це спосіб відкласти роботу до наступної ітерації Event Loop: дати браузеру перемалювати інтерфейс, розбити важкий синхронний код або скинути стек у рекурсії. ```javascript console.log('A'); setTimeout(() => console.log('B'), 0); console.log('C'); // A, C, B ``` **Ключове:** нульова затримка ставить колбек у чергу макрозадач, тож він виконається після синхронного коду і після всіх мікрозадач.Показується над повною відповіддю для швидкого нагадування.Відповідь (UA)Зображення**`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`.** Відкладений колбек може спрацювати вже після того, як компонент знято з екрана, і звернутися до неактуальних даних.Для рев’юераПримітка для модератора (необов’язково)Бачить лише модератор. Прискорює рев’ю.