Що таке event loop
Event loop (цикл подій) - це механізм усередині рушія JavaScript, який керує виконанням коду, обробкою асинхронних операцій та взаємодією між стеком викликів (call stack) і чергами завдань (task queues). Саме event loop робить JavaScript асинхронним і неблокуючим, попри те що сама мова однопотокова.
Теорія
TL;DR
- Event loop вирішує, яка задача виконується зараз, а яка почекає, щоб не блокувати головний потік.
- Асинхронний колбек потрапляє в стек лише тоді, коли стек порожній.
- Мікротаски (
Promise.then,queueMicrotask) мають вищий пріоритет за макротаски (setTimeout, події, I/O). - Після кожної макротаски рушій спустошує всю чергу мікротасків і лише потім бере наступну макротаску.
- Якщо синхронний код не завершується, event loop не дійде до черг узагалі.
Швидкий приклад
console.log('1');
setTimeout(() => console.log('2'), 0);
Promise.resolve().then(() => console.log('3'));
console.log('4');Вивід:
1
4
3
2Чому саме так:
- Спочатку виконується синхронний код (
1,4). - Колбек промісу (
.then) потрапляє в чергу мікротасків. setTimeoutпотрапляє в чергу макротасків.- Event loop спершу виконує всі мікротаски (
3), а потім бере наступну макротаску (2).
Основні учасники
Щоб зрозуміти event loop, треба знати чотири структури:
- Call Stack (стек викликів) - там виконується поточний код: функції та вирази.
- Task Queue / Callback Queue - черга макротасків: таймери, події,
setTimeout,setInterval, I/O. - Microtask Queue - черга мікротасків:
Promise.then(),queueMicrotask(), а в Node.js щеprocess.nextTick(). - Event Loop - «петля», яка стежить: якщо стек порожній, бере задачу з черги мікротасків; якщо та порожня, бере задачу з черги макротасків.
Схематично:
+--------------------+
| Call Stack | <- runs the current code
+---------^----------+
| (empty?)
v
+--------------------+
| Microtask Queue | <- Promise, queueMicrotask
+---------^----------+
| (empty?)
v
+--------------------+
| Task Queue | <- setTimeout, setInterval, I/O
+--------------------+
Event loop checks the queues and moves jobs into the stackЯк працює event loop покроково
- JavaScript виконує синхронний код, зверху вниз.
- Коли трапляється асинхронна операція, вона передається зовнішнім Web API (наприклад, таймер або
fetch). - Коли операція завершується, її колбек потрапляє у відповідну чергу завдань.
- Коли стек викликів порожній, event loop бере задачу з черги та кладе її в стек на виконання.
- Після виконання всіх мікротасків event loop переходить до наступної макротаски.
Це відбувається постійно, у нескінченному циклі.
Якщо стек ніколи не звільняється, черги не обробляються зовсім:
setTimeout(() => console.log('2'), 0);
while (true) {} // infinite loopEvent loop ніколи не дійде до setTimeout, бо стек ніколи не звільниться. Асинхронні задачі виконуються лише тоді, коли стек порожній.
Мікротаски проти макротасків
| Тип задачі | Приклади | Коли виконується |
|---|---|---|
| Мікротаски | Promise.then, queueMicrotask | одразу після поточного коду і перед наступною макротаскою |
| Макротаски | setTimeout, setInterval, fetch, I/O, DOM events | після всіх мікротасків |
Тобто мікротаски мають вищий пріоритет.
У браузері та в Node.js
| Середовище | Особливості event loop |
|---|---|
| Браузер | Web APIs дають таймери, fetch, події DOM |
| Node.js | має власні фази (phases): timers, I/O, check, close, nextTick тощо |
Але в обох випадках ідея одна: один потік, асинхронність через event loop.
Підсумок
| Компонент | Призначення |
|---|---|
| Call Stack | виконує поточний код |
| Web APIs | відпрацьовують асинхронні операції |
| Task Queue | зберігає колбеки макротасків |
| Microtask Queue | зберігає проміси та мікрочергу |
| Event Loop | переносить готові задачі з черг у стек |
Типові помилки
- Вважати, що
setTimeout(fn, 0)виконаєfnнегайно. Затримка 0 означає лише «якнайшвидше після звільнення стека і всіх мікротасків». - Думати, що event loop є частиною самої мови. Це механізм середовища виконання: браузера або Node.js, а не специфікації ECMAScript.
- Плутати пріоритети: проміс, створений пізніше за таймер, усе одно виконається раніше, бо мікротаски йдуть першими.
- Породжувати мікротаски всередині мікротасків нескінченно: черга мікротасків спустошується повністю, тому такий цикл заморозить сторінку так само, як
while (true). - Очікувати, що
setTimeout(fn, 100)спрацює рівно через 100 мс. Це мінімальна затримка, реальний час залежить від завантаженості стека.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.