Каков порядок выполнения setTimeout и промисов в JavaScript при вложенных вызовах
Что проверяют
Держите ли вы в голове модель цикла событий, а не заученную мантру «промисы раньше таймеров». Вложенность в вопросе не случайна: интервьюер проверяет, понимаете ли вы, что очередь микрозадач вычерпывается полностью после каждой макрозадачи, включая микрозадачи, появившиеся по ходу. Следом обычно дают листинг и просят предсказать вывод.
Как отвечать
Сформулируйте правило и покажите его на вложенном примере.
- синхронный код выполняется до конца, только потом берётся что-либо из очередей;
- микрозадачи: колбэки
then,catch,finally, продолжение послеawait,queueMicrotask, обработчикиMutationObserver. Очередь опустошается целиком, и микрозадача, поставившая новую микрозадачу, отработает в том же проходе; - макрозадачи:
setTimeout,setInterval, обработчики событий, сетевые колбэки. За один оборот цикла берётся одна макрозадача, после неё снова полный дренаж микрозадач; - отрисовку браузер делает между макрозадачами, поэтому бесконечная цепочка микрозадач подвешивает страницу, а цепочка
setTimeoutнет.
Разберите вложенный случай: два setTimeout с нулевой задержкой, и внутри первого Promise.resolve().then(...). Порядок будет такой: первый таймер, его промис, второй таймер. Микрозадачи первой макрозадачи выполняются до старта второй.
Полезные детали: await это синтаксис поверх then, и после оптимизации ES2019 ожидание нативного промиса стоит один тик, а не три. setTimeout(fn, 0) не значит «сейчас»: по спецификации при вложенности глубже пяти уровней задержка поднимается до 4 мс, а в фоновой вкладке таймеры тормозятся дополнительно. В Node.js картина отличается: process.nextTick идёт вперёд промисов, а setImmediate попадает в фазу check.
Где спотыкаются
Говорят «микрозадачи выполняются раньше макрозадач» и на этом останавливаются. Вопрос был про вложенность, а там важен именно момент дренажа очереди.
Считают, что setTimeout(fn, 0) сработает раньше промиса, созданного позже по коду. Нулевая задержка это заявка на следующий оборот цикла, а не приоритет.
Путают порядок в Node.js и в браузере. Про process.nextTick и setImmediate спрашивают ровно тех, кто заявил бэкенд на Node.