Event Loop: микро- и макрозадачи
↑ 2.1 JavaScript: продвинутый · ← Предыдущая · → Следующая
Зачем это на собесе
«Что выведет код?» с
setTimeout,Promiseиasync/await— почти обязательная задача.
Объяснение
JS однопоточный. Event loop координирует стек вызовов, очереди задач и среду (Web APIs/Node).
Цикл:
- Выполнить текущую задачу (macro task) до опустошения стека.
- Выполнить все микрозадачи (очередь полностью, включая добавленные по ходу).
- (Браузер) при необходимости выполнить рендеринг.
- Взять следующую макрозадачу.
| Макрозадачи | Микрозадачи |
|---|---|
скрипт, setTimeout, setInterval, события UI, MessageChannel, I/O | Promise.then/catch/finally, продолжение после await, queueMicrotask, MutationObserver |
console.log("1");
setTimeout(() => console.log("2"), 0);
Promise.resolve().then(() => console.log("3"));
queueMicrotask(() => console.log("4"));
(async () => { console.log("5"); await null; console.log("6"); })();
console.log("7");
// 1, 5, 7, 3, 4, 6, 2Разбор: синхронно — 1, 5, 7; микрозадачи по порядку постановки — 3, 4, 6; затем макрозадача — 2.
Следствия:
- Бесконечные микрозадачи блокируют рендер и таймеры.
setTimeout(fn, 0)выполняется не мгновенно (минимальная задержка, после микрозадач и текущей задачи).- Тяжёлый синхронный код блокирует всё: разбивайте на части (
setTimeout/scheduler.yield), выносите в Web Workers.
В Node.js фазы иные: timers → pending → poll → check (setImmediate) → close, между фазами — process.nextTick и микрозадачи.
Нюансы и подводные камни
await xэквивалентенPromise.resolve(x).then(...): продолжение — микрозадача (число тиков зависит от типа значения).- Рендер не происходит между микрозадачами.
setIntervalне гарантирует точный интервал.- Порядок
setTimeoutиsetImmediateв главном модуле Node не определён.
Практика
- Решите 10 задач «что выведет код» и объясните порядок вслух.
- Разбейте тяжёлый цикл на части, чтобы страница оставалась отзывчивой.
- Перенесите вычисление в Web Worker.
Вопросы с ответами
Чем микрозадачи отличаются от макрозадач?
Очередь микрозадач полностью выполняется после каждой макрозадачи и до рендера; макрозадачи берутся по одной.
Что раньше:
setTimeout(0)илиPromise.then?
Promise.then— микрозадача выполнится до макрозадачиsetTimeout.