Пошук уроків, статей та іншого контенту
Як JavaScript виконує асинхронний код: call stack, черга задач, мікрозадачі та макрозадачі.
JavaScript виконує код в одному потоці — за раз може виконуватись лише одна операція. Проте завдяки Event Loop мова може обробляти асинхронні операції (таймери, мережеві запити, події) без блокування основного потоку.
Call Stack (стек викликів) — де виконується поточний код, функція за функцією.
Web APIs / Node APIs — середовище (браузер або Node.js) обробляє таймери, запити тощо окремо від стека.
Черги задач (Task Queue та Microtask Queue) — завершені асинхронні операції чекають своєї черги на виконання.
Event Loop постійно перевіряє: якщо call stack порожній, він бере наступне завдання з черги мікрозадач (Promise), а потім — з черги макрозадач (setTimeout, події).
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
console.log("4");
// Результат: 1, 4, 3, 2Синхронний код (1, 4) виконується першим. Далі Event Loop спочатку спорожнює чергу мікрозадач — тому Promise (3) виконується раніше за setTimeout (2), навіть із затримкою 0 мс.
Promise, async/await та queueMicrotask потрапляють у чергу мікрозадач, яка повністю спорожнюється перед кожною макрозадачею (setTimeout, setInterval, події DOM).
Якщо мікрозадача постійно додає нові мікрозадачі, черга макрозадач (і рендеринг у браузері) може «зголодувати» — це реальна причина продуктивних багів.
Очікування, що setTimeout(fn, 0) виконається негайно — насправді він завжди чекає завершення поточного синхронного коду і всіх мікрозадач.
Ланцюжки Promise, які створюють нескінченну чергу мікрозадач і «блокують» рендеринг.
Плутанина між блокуючим синхронним кодом (наприклад, важкий цикл) і асинхронними операціями — Event Loop не рятує від довгих синхронних обчислень.
Event Loop дозволяє однопотоковому JavaScript обробляти асинхронні операції без блокування: call stack виконує синхронний код, Web/Node API обробляють таймери й запити окремо, а черга мікрозадач (Promise) завжди спорожнюється повністю перед кожною макрозадачею (setTimeout). Це пояснює, чому Promise.then() із затримкою 0 виконується раніше за setTimeout(fn, 0), навіть якщо обидва «мали б» виконатись негайно. Наступні уроки цього модуля покажуть Promise — API, що і породжує мікрозадачі, про які тут ішлося.
Спробуйте самостійно
Запишіть код із трьома console.log: один звичайний, один усередині setTimeout(fn, 0) і один усередині Promise.resolve().then(fn). Перед запуском спробуйте вгадати порядок виводу, а потім перевірте себе.