> Каков порядок выполнения синхронных задач, микротасков и макротасков в JavaScript (JavaScript)

Уровень: senior · Роль: frontend · Категория: Технические вопросы

Компании: amoCRM, IT-One, Библио-Глобус

Стек: JavaScript

> Пример ответа

Короткий ответ

В JavaScript порядок выполнения определяется event loop: сначала выполняется весь синхронный код (текущий call stack), затем - все микротаски (Promise.then, queueMicrotask, MutationObserver), и только после полного опустошения очереди микротасков - один макротаск (setTimeout, setInterval, I/O, рендеринг). После каждого макротаска цикл повторяется: снова все микротаски, затем следующий макротаск. Микротаски всегда имеют приоритет над макротасками.

Подробное объяснение

Event loop работает по строгому алгоритму:

  1. Синхронный код - выполняется полностью, пока call stack не опустеет. Это включает весь текущий скрипт и вызовы функций.
  2. Микротаски - после каждого синхронного блока и после каждого макротаска. Очередь микротасков обрабатывается до полного опустошения. Если во время обработки микротаска добавляет новый микротаск, он тоже выполнится в этой же итерации.
  3. Макротаски - берётся только один макротаск из очереди за итерацию. После его выполнения снова обрабатываются все микротаски.

Ключевое отличие: микротаски - это "быстрые" продолжения, которые должны выполниться до следующего рендеринга или I/O. Макротаски - это отложенные задачи, которые могут ждать.

Важно: рендеринг браузера происходит между макротасками, но не между микротасками. Поэтому если в микротаске запустить бесконечный цикл, страница зависнет - рендеринг не случится.

На практике

Для senior-позиции важно не просто назвать порядок, а объяснить последствия:

  • Промисы создают микротаски, поэтому их then-цепочки выполняются до следующего макротаска. Это значит, что несколько Promise.resolve().then() подряд выполнятся "батчем", без вставки между ними других задач.
  • setTimeout(fn, 0) не гарантирует выполнение через 0 мс - он попадёт в очередь макротасков и выполнится после всех текущих микротасков.
  • async/await - это синтаксический сахар над промисами: await создаёт микротаск для продолжения функции.
  • requestAnimationFrame - это отдельный тип задач, выполняемый перед рендерингом, но после микротасков. Его нельзя путать с макротасками.
  • Для тяжёлых вычислений лучше использовать Web Workers или разбивать на части через setTimeout, чтобы дать браузеру возможность отрендерить.

Пример кода

JAVASCRIPT
console.log('1: sync');
setTimeout(() => {
console.log('4: macro');
Promise.resolve().then(() => console.log('5: micro inside macro'));
}, 0);
Promise.resolve()
.then(() => console.log('2: micro'))
.then(() => console.log('3: micro chain'));
// Вывод:
// 1: sync
// 2: micro
// 3: micro chain
// 4: macro
// 5: micro inside macro

Объяснение: сначала синхронный код, затем все микротаски (включая цепочку), затем один макротаск, и после него - его микротаск.

Как отвечать на собеседовании

Начните с чёткого порядка: синхронный код → микротаски → макротаск → микротаски → макротаск. Затем добавьте детали: почему микротаски приоритетнее, что происходит с рендерингом, как ведут себя async/await. Приведите пример с выводом в консоль и объясните его. Если спросят про Node.js - упомяните, что там нет рендеринга, но порядок тот же, плюс есть process.nextTick, который выполняется до остальных микротасков.

Что проверяет интервьюер

Интервьюер проверяет:

  • понимание event loop как механизма, а не заученного порядка;
  • способность предсказать порядок вывода в сложных примерах с вложенными промисами и таймерами;
  • знание отличий между браузером и Node.js;
  • понимание влияния микротасков на производительность и рендеринг;
  • умение объяснить, почему нельзя блокировать микротаски.

Типичные ошибки

  • Утверждение, что setTimeout(0) выполнится сразу после синхронного кода - на самом деле после всех микротасков.
  • Забывают, что микротаски, добавленные внутри микротаска, выполняются в той же итерации.
  • Путают requestAnimationFrame с макротасками - это отдельный тип.
  • Не учитывают process.nextTick в Node.js - он выполняется до Promise.then.
  • Считают, что макротаски выполняются "по одному за раз" в строгом порядке FIFO - на самом деле порядок зависит от типа таймера и задержки, но в рамках одной задержки - FIFO.
  • Не упоминают рендеринг - для фронтенда это критично.

> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?

Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью