Skip to main content

Що таке event loop

Event loop (цикл подій) - це механізм усередині рушія JavaScript, який керує виконанням коду, обробкою асинхронних операцій та взаємодією між стеком викликів (call stack) і чергами завдань (task queues). Саме event loop робить JavaScript асинхронним і неблокуючим, попри те що сама мова однопотокова.

Теорія

TL;DR

  • Event loop вирішує, яка задача виконується зараз, а яка почекає, щоб не блокувати головний потік.
  • Асинхронний колбек потрапляє в стек лише тоді, коли стек порожній.
  • Мікротаски (Promise.then, queueMicrotask) мають вищий пріоритет за макротаски (setTimeout, події, I/O).
  • Після кожної макротаски рушій спустошує всю чергу мікротасків і лише потім бере наступну макротаску.
  • Якщо синхронний код не завершується, event loop не дійде до черг узагалі.

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

javascript
console.log('1'); setTimeout(() => console.log('2'), 0); Promise.resolve().then(() => console.log('3')); console.log('4');

Вивід:

javascript
1 4 3 2

Чому саме так:

  1. Спочатку виконується синхронний код (1, 4).
  2. Колбек промісу (.then) потрапляє в чергу мікротасків.
  3. setTimeout потрапляє в чергу макротасків.
  4. Event loop спершу виконує всі мікротаски (3), а потім бере наступну макротаску (2).

Основні учасники

Щоб зрозуміти event loop, треба знати чотири структури:

  1. Call Stack (стек викликів) - там виконується поточний код: функції та вирази.
  2. Task Queue / Callback Queue - черга макротасків: таймери, події, setTimeout, setInterval, I/O.
  3. Microtask Queue - черга мікротасків: Promise.then(), queueMicrotask(), а в Node.js ще process.nextTick().
  4. Event Loop - «петля», яка стежить: якщо стек порожній, бере задачу з черги мікротасків; якщо та порожня, бере задачу з черги макротасків.

Схематично:

javascript
+--------------------+ | 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 покроково

  1. JavaScript виконує синхронний код, зверху вниз.
  2. Коли трапляється асинхронна операція, вона передається зовнішнім Web API (наприклад, таймер або fetch).
  3. Коли операція завершується, її колбек потрапляє у відповідну чергу завдань.
  4. Коли стек викликів порожній, event loop бере задачу з черги та кладе її в стек на виконання.
  5. Після виконання всіх мікротасків event loop переходить до наступної макротаски.

Це відбувається постійно, у нескінченному циклі.

Якщо стек ніколи не звільняється, черги не обробляються зовсім:

javascript
setTimeout(() => console.log('2'), 0); while (true) {} // infinite loop

Event 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 мс. Це мінімальна затримка, реальний час залежить від завантаженості стека.

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

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

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