Skip to Content
JavaScriptEvent loop

Event loop

Уровень: middle. На senior спрашивают, как это видно в интерфейсе.

Вопрос

Чем microtask отличается от macrotask, и в каком порядке выполнится код с setTimeout, Promise.then и обычным вызовом?

Короткий ответ

Синхронный код выполняется сразу, в текущем стеке. Когда стек пустеет, event loop сначала опустошает очередь микрозадач, и только потом берёт одну макрозадачу. Promise.then, queueMicrotask и MutationObserver — микрозадачи. setTimeout, setInterval, события ввода и большинство колбэков I/O — макрозадачи.

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

Разбор

Движок не «ждёт таймер в фоне внутри JS». Хост (браузер или Node) кладёт колбэк в очередь, когда таймер сработал. Даже при задержке 0 колбэк setTimeout не влезет перед микрозадачами, которые уже запланированы.

Микрозадачи, поставленные из микрозадачи, тоже выполнятся до следующей макрозадачи. Бесконечный then может надолго отложить отрисовку и обработку клика.

async-функция до первого await идёт синхронно. После await продолжение попадает в микрозадачи, потому что await разворачивается через промис.

На что смотрят на senior

Связка с UI: тяжёлая микрозадача блокирует кадр так же, как длинный синхронный цикл. setTimeout(fn, 0) не «отдаёт поток немедленно» в смысле микрозадач, но даёт браузеру шанс взять следующую макрозадачу, включая отрисовку, если микроочередь пуста. Для разбиения длинной работы ближе scheduler.yield, requestAnimationFrame или нарезка на чанки, а не надежда на нулевой таймер.

В Node к макрозадачам добавляются setImmediate и фазы poll, но на фронтенд-собеседовании обычно хватает браузерной модели: стек, микрозадачи, затем рендер и макрозадачи. Точный порядок «рендер между макрозадачами» стоит оговорить: браузер старается отрисовать кадр, когда очередь микрозадач пуста и нет более срочной работы.

Типичные ошибки

  • Считать, что setTimeout(0) выполнится раньше уже созданного Promise.then.
  • Путать очередь микрозадач с очередью таймеров.
  • Думать, что async делает всю функцию асинхронной с первой строки.

Задача

В каком порядке появятся строки?

console.log('A') setTimeout(() => console.log('B'), 0) Promise.resolve().then(() => console.log('C')) console.log('D')
Показать ответ

Синхронный код идёт сразу: A, затем D. then — микрозадача, она выполняется раньше таймера. B — макрозадача, она последняя.

Порядок: A, D, C, B.

Last updated on