Каков порядок вывода консольных логов при использовании setTimeout и промисов в JavaScript
Что проверяют
Понимаете ли вы event loop на уровне очередей: синхронный код, микрозадачи, макрозадачи. Обычно дают фрагмент с setTimeout(..., 0), Promise.resolve().then и async/await и просят написать порядок логов. Следом спрашивают, что изменится, если внутри then поставить ещё один таймер или промис.
Как отвечать
Правило из трёх шагов:
- синхронный код: выполняется сразу, до конца текущего стека. Сюда же относится функция-исполнитель в
new Promise(executor): она вызывается синхронно, асинхронны только обработчикиthen,catch,finally; - микрозадачи: колбэки промисов, код после
await,queueMicrotask,MutationObserver. Очередь выбирается целиком после каждой задачи, включая микрозадачи, добавленные по ходу; - макрозадачи:
setTimeout,setInterval, события, сообщения. За один оборот цикла берётся одна задача, затем снова вся очередь микрозадач, затем браузер может отрисовать кадр.
Пример: console.log(1); setTimeout(() => console.log(2)); Promise.resolve().then(() => console.log(3)); console.log(4) выведет 1, 4, 3, 2.
Про async: функция до первого await выполняется синхронно, как обычный код. Всё после await уходит в микрозадачу, даже если ожидаемое значение уже готово.
Про таймер: задержка означает не «через N мс», а «не раньше чем через N мс». Колбэк встанет в очередь по истечении срока и дождётся, пока освободится стек и опустеет очередь микрозадач. При глубокой вложенности таймеров браузеры поднимают минимальную задержку до 4 мс.
В Node.js добавьте process.nextTick: его очередь обрабатывается раньше промисов.
Где спотыкаются
Считают исполнитель промиса асинхронным и печатают лог из new Promise(() => console.log(...)) после синхронного кода.
Думают, что setTimeout(fn, 0) выполнится сразу после текущей строки. Он выполнится после всех микрозадач, а бесконечная цепочка микрозадач вообще не даст ему шанса.
Путают порядок во вложенных конструкциях: таймер, созданный внутри then, встаёт в очередь позже таймеров с той же задержкой, созданных раньше.