Практика: задача на последовательность вызовов микро/макро таски

// Каков будет порядок логов и почему? setTimeout(() => console.log(2), 0); console.log(1); new Promise((res) => { console.log(6); res(); console.log(3); }) .then(() => { console.log(4); setTimeout(() => console.log(7), 0); }); console.log(5);
 
🎯 Зачем спрашивают
Проверка базового понимания работы event loop, микротасок и макротасок.
 
📝 Ответ
Прежде чем приступать к задаче, давайте вспомним порядок выполнения кода:
  1. синхронный код
  1. микротаски — Promise , Mutation Observer
  1. макротаски — API окружения браузер/Node.js (setTimeout, setInterval , setImmediate)
 
💡
Точнее о порядке выполнения
После завершения текущего стека синхронного кода движок полностью вычищает очередь микротасок, затем (в браузере) может произойти шаг рендеринга, и только после этого выполняется следующая макротаска
 
Пройдемся по коду и расставим порядок выполнения. Сперва найдем весь синхронный код:
setTimeout(() => console.log(2), 0); // <-- Ставим макротаску #1 console.log(1); // 1 new Promise((res) => { console.log(6); // 2 res(); // <-- Ставим микротаску #1 console.log(3); // 3 }) .then(() => { console.log(4); setTimeout(() => console.log(7), 0); }); console.log(5); // 4
 
⚠️
Не забудьте, что конструктор Promise выполняется синхронно!
Мы разметили синхронный код. После синхронного кода у нас уже запланировано:
  • 1 микротаска → .then
  • 1 макротаска → setTimeout(() => console.log(2)))
 
Далее идет выполнение микротасок:
setTimeout(() => console.log(2), 0); console.log(1); // 1 new Promise((res) => { console.log(6); // 2 res(); console.log(3); // 3 }) .then(() => { console.log(4); // 5 <--! setTimeout(() => console.log(7), 0); // <-- Ставим макротаску #2 }); console.log(5); // 4
Далее выполняются макротаски в порядке постановки:
setTimeout(() => console.log(2), 0); // 6 <--! console.log(1); // 1 new Promise((res) => { console.log(6); // 2 res(); console.log(3); // 3 }) .then(() => { console.log(4); // 5 setTimeout(() => console.log(7), 0); // 7 <--! }); console.log(5); // 4
Итоговый лог будет (что будет в консоли):
// 1 6 3 5 4 2 7
 
 
⚖️ Компромиссы
✅ Плюсы
❌ Минусы
Понимание event loop помогает писать предсказуемый код и избегать багов в async логике.
Такие вопросы часто тестируют память, а не реальный навык. Лучше уметь объяснить принцип, а не просто порядок.
 
 
🔎 Встречные вопросы
  • Что такое starvation микротасок и как её не допустить?
  • Для чего нужен queueMicrotask()? Какие способы использования?
 
🚩 Красные флаги
  • Кандидат говорит, что Promise выполняется асинхронно «всегда» (не понимает, что конструктор — синхронный).
  • Путает микротаски и макротаски.
  • Называет setTimeout(..., 0) «выполнением сразу после кода».
 
🛠 Практика
Задача 1
// Каков порядок логов? Почему? console.log(1); setTimeout(() => console.log(2), 0); Promise.resolve().then(() => console.log(3)); console.log(4);
Задача 2
// Каков порядок логов? Почему? console.log('A'); setTimeout(() => console.log('B'), 0); queueMicrotask(() => console.log('C')); console.log('D');
Задача 3
// Каков порядок логов? Почему? Promise.resolve() .then(() => { console.log('A'); Promise.resolve().then(() => console.log('B')); }) .then(() => console.log('C'));
Задача 4
// Каков порядок логов? Почему? setTimeout(() => console.log('1'), 0); Promise.resolve() .then(() => console.log('2')) .then(() => console.log('3')); queueMicrotask(() => console.log('4')); console.log('5');
 
 
📚 Источники / ссылки
  • MDN Web DocsMDN Web DocsJavaScript execution model - JavaScript | MDN