Каков порядок выполнения кода с setTimeout, Promise и цепочкой then/catch в JavaScript?
Что проверяют
Понимаете ли вы, что асинхронность в JavaScript это не параллельность, а порядок разбора очередей одним потоком. Вопрос почти всегда идёт с листингом, где надо предсказать вывод. Следом добавляют async/await или вложенный setTimeout внутри then и смотрят, объясняете вы правило или вспоминаете заученный ответ.
Как отвечать
Опишите механику, а потом примените её к коду:
- синхронный код: выполняется до конца текущей задачи. Функция-исполнитель в
new Promise(fn)вызывается синхронно, прямо на месте, асинхронны только обработчики; - микрозадачи: коллбэки
then,catch,finally,queueMicrotask, продолжение послеawait. Очередь вычерпывается полностью после текущей задачи, вместе с микрозадачами, добавленными по ходу разбора; - макрозадачи:
setTimeout,setInterval, обработчики событий, ввод-вывод. Между ними браузер успевает отрисовать кадр, аsetTimeout(fn, 0)после пяти вложенных вызовов зажимается минимум до 4 мс; - цепочка: каждое звено
thenэто отдельный тик микрозадачи, поэтому две параллельные цепочки чередуются, а не проходят подряд.
Для классического листинга порядок такой: Start, End, Promise, Timeout. Формулировка правила одна: сначала синхронный код, затем все микрозадачи, затем одна макрозадача, и снова все микрозадачи.
Если позиция серверная, упомяните Node.js: там свои фазы цикла, process.nextTick разбирается раньше очереди промисов, а setImmediate попадает в фазу check и в ряде случаев обгоняет setTimeout(fn, 0).
Где спотыкаются
Думают, что setTimeout(fn, 0) выполнится сразу и раньше промиса. Ноль это не задержка, а место в конце очереди макрозадач, и всё, что накопилось в микрозадачах, отработает раньше.
Считают тело new Promise(...) асинхронным. Оно исполняется немедленно, и console.log внутри выведется до строки, стоящей после промиса.
Заучивают последовательность для одного примера. Достаточно поставить await внутри цикла или второй then после catch, и ответ разваливается: catch возвращает выполненный промис, поэтому следующий then в цепочке тоже отработает.