В каком порядке выполняются console.log в JavaScript с учетом синхронности и микротасков?
Что проверяют
Умеете ли вы по коду предсказать порядок выполнения, а не пересказывать схему событийного цикла. Обычно дают фрагмент с setTimeout, Promise и async/await, просят назвать вывод, а потом объяснить, почему бесконечная цепочка микрозадач вешает страницу.
Как отвечать
Правило такое: сначала целиком выполняется текущий синхронный код, затем очередь микрозадач опустошается полностью, затем браузер может отрисовать кадр, и только потом берётся одна макрозадача, после которой снова разбираются микрозадачи.
- микрозадачи: колбэки
then,catch,finally, продолжение послеawait,queueMicrotask,MutationObserver. Микрозадача, поставленная во время разбора очереди, выполнится в этом же проходе; - макрозадачи:
setTimeout,setInterval, события ввода, сообщенияMessageChannel; - синхронные ловушки: функция-исполнитель в
new Promise(...)выполняется сразу, аasync-функция идёт синхронно до первогоawait.
Разберите пример вслух:
console.log(1);
setTimeout(() => console.log(2), 0);
Promise.resolve().then(() => console.log(3));
(async () => {
console.log(4);
await null;
console.log(5);
})();
console.log(6);Вывод: 1, 4, 6, 3, 5, 2. Синхронно печатаются 1, 4 и 6, затем микрозадачи в порядке постановки: 3 и продолжение после await с 5. Последней идёт макрозадача с 2.
Если вопрос про Node.js, добавьте, что очередь process.nextTick разбирается раньше промисов, а порядок setTimeout(fn, 0) и setImmediate в главном модуле не гарантирован.
Где спотыкаются
Считают исполнитель промиса асинхронным и ставят вывод из new Promise(resolve => { console.log(7) }) после синхронного кода.
Думают, что за один оборот цикла выполняется одна микрозадача. Выполняются все, включая добавленные по ходу, поэтому рекурсивный then не даст отрисовать ни одного кадра.
Воспринимают setTimeout(fn, 0) как «сразу после текущего кода». Это макрозадача, она ждёт всех микрозадач, а у глубоко вложенных таймеров браузер поднимает минимальную задержку до 4 мс.