Детально про Event loop
Що таке Event Loop?
Event Loop - механізм, який організовує виконання JS-коду в однопотоковому середовищі: бере задачі, запускає їх, після кожної задачі прогонює мікрозадачі, за потреби дає браузеру відрендерити, потім бере наступну задачу. Це "диригент" між стеком викликів, чергами задач і відмальовуванням.
Спрощено (браузер):
while (true) {
взяти одну macrotask → виконати
поки є microtask → виконувати по одній (FIFO)
якщо можна → виконати rAF/рендер
}Що робить call stack?
Call stack (стек викликів) - це LIFO-структура, де виконуються функції "тут і зараз".
- Викликали функцію - вона пушиться в стек.
- Функція завершилася - поп зі стека.
- Поки стек не порожній, жодні колбеки з черг виконуватися не можуть.
Що робить microtask queue?
Microtask queue - черга "термінових" коротких задач, які виконуються одразу після поточної задачі (після спорожнення стека) і до наступної macrotask/рендера.
- Приклади джерел:
Promise.then/catch/finally,queueMicrotask,MutationObserver(браузер),process.nextTick(Node.js: окрема надмікрочерга). - Важливо: усі мікрозадачі виконуються поспіль. Довгий ланцюжок мікрозадач може заблокувати рендер (starvation).
Що робить macrotask queue?
Macrotask queue - черга "звичайних" подій/задач: таймери, I/O, UI-події.
- Приклади (браузер):
setTimeout,setInterval,MessageChannel, користувацькі події, мережеві колбеки. - Приклади (Node.js):
setTimeout/setInterval(timers),setImmediate(check), I/O callbacks та ін. - Event Loop бере одну macrotask, виконує її повністю, потім спорожнює microtask queue.
Чим microtasks відрізняються від macrotasks?
- Момент виконання: microtasks - одразу після поточної задачі і до рендера; macrotasks - по одній за "тик" циклу.
- Пріоритет: microtasks мають вищий пріоритет (вони "вклинюються" між macrotasks).
- Типові джерела: microtasks -
Promise/queueMicrotask; macrotasks -setTimeout, події UI/I/O. - Ризик: нескінченні microtasks можуть затримати відмальовування й відгук інтерфейсу.
Що робить queueMicrotask()?
Планує колбек у microtask queue:
- Виконається після поточного стека, до наступної macrotask і до рендера.
- По суті - "як
Promise.then, але без створення зайвого проміса".
console.log('A');
queueMicrotask(() => console.log('microtask-1'));
Promise.resolve().then(() => console.log('microtask-2'));
setTimeout(() => console.log('macrotask (timeout)'), 0);
console.log('B');
// Порядок виводу:
// A
// B
// microtask-1
// microtask-2
// macrotask (timeout)Чому так? Спочатку виконується скрипт (одна macrotask): друкуємо A, ставимо мікрозадачі, ставимо макрозадачу, друкуємо B. Потім усі мікрозадачі (queueMicrotask/Promise), і лише потім -
setTimeout.
Ще кілька практичних заміток
- У браузері рендер зазвичай відбувається після спорожнення microtask queue і між macrotasks. Довгі мікрозадачі = "фризи".
- У Node.js є фази циклу (timers → pending callbacks → poll → check → close). Мікрозадачі виконуються після кожного колбека і наприкінці тіку.
process.nextTickвиконується навіть раніше за звичайні microtasks - не зловживай цим. requestAnimationFrame- не micro/macrotask; це кадр відмальовування, який настає після мікрозадач і перед рендером.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.