> Каков порядок выполнения задач (task) и микротасков (microtask) в JavaScript (JavaScript)
Уровень: senior · Роль: frontend · Категория: Технические вопросы
Компании: циан, IT-One, Purrweb, SmartWay
Стек: JavaScript
> Пример ответа
Короткий ответ
В JavaScript event loop сначала выполняет один макротаск (task) из очереди, затем обрабатывает всю очередь микротасков (microtask), после чего переходит к рендерингу (если требуется), и повторяет цикл. Микротаски имеют приоритет над макротасками и выполняются до начала следующего макротаска. Это гарантирует, что промисы и MutationObserver обрабатываются быстрее, чем setTimeout или события ввода.
Подробное объяснение
Event loop в JavaScript работает по следующему алгоритму:
- Выполняется текущий синхронный код (глобальный скрипт - это тоже макротаск).
- Из очереди макротасков (task queue) извлекается один самый старый макротаск и выполняется.
- После завершения макротаска проверяется очередь микротасков (microtask queue). Все микротаски выполняются последовательно, пока очередь не опустеет. Если во время выполнения микротаска добавляются новые микротаски, они тоже выполняются в этом же цикле.
- После очистки очереди микротасков может произойти рендеринг (обновление DOM, вычисление стилей, repaint), если браузер решит, что это необходимо.
- Цикл повторяется: берётся следующий макротаск.
К макротаскам относятся: setTimeout, setInterval, setImmediate (Node.js), I/O, события UI (click, keydown), рендеринг.
К микротаскам относятся: Promise.then/catch/finally, queueMicrotask, MutationObserver, process.nextTick (Node.js).
Важно: микротаски могут блокировать event loop, если они добавляют новые микротаски бесконечно - браузер не сможет перейти к рендерингу или следующему макротаску.
На практике
- Promise.then выполняется до setTimeout с нулевой задержкой, даже если setTimeout был вызван раньше.
- Если в микротаске создать новый промис и зарезолвить его, его then тоже выполнится в текущем цикле микротасков.
- В Node.js process.nextTick имеет ещё более высокий приоритет, чем Promise.then, и выполняется до других микротасков.
- MutationObserver срабатывает как микротаск, поэтому изменения DOM обрабатываются до следующего рендеринга.
- Для асинхронных операций, которые не требуют немедленного выполнения, лучше использовать макротаски (setTimeout), чтобы не блокировать UI.
Пример кода
JAVASCRIPTconsole.log('1: синхронный код');setTimeout(() => {console.log('2: макротаск (setTimeout)');}, 0);Promise.resolve().then(() => {console.log('3: микротаск (Promise.then)');});queueMicrotask(() => {console.log('4: микротаск (queueMicrotask)');});console.log('5: ещё синхронный код');// Вывод:// 1: синхронный код// 5: ещё синхронный код// 3: микротаск (Promise.then)// 4: микротаск (queueMicrotask)// 2: макротаск (setTimeout)
Как отвечать на собеседовании
Начни с краткого определения event loop и приоритета микротасков. Затем опиши пошаговый алгоритм выполнения. Обязательно приведи пример с промисом и setTimeout, чтобы показать разницу. Упомяни, что микротаски могут блокировать поток, если их бесконечно добавлять. Если спросят про Node.js, добавь про process.nextTick. Не углубляйся в детали реализации V8, если не попросят.
Что проверяет интервьюер
- Понимание основ event loop и асинхронности в JavaScript.
- Умение различать макротаски и микротаски.
- Знание, какие API относятся к каждой категории.
- Понимание, что микротаски выполняются до рендеринга и следующего макротаска.
- Способность предсказать порядок вывода в коде с асинхронными операциями.
Типичные ошибки
- Утверждение, что микротаски выполняются после всех макротасков - это неверно, они выполняются после каждого макротаска.
- Путаница между setTimeout(fn, 0) и Promise.resolve().then - многие думают, что setTimeout выполнится первым, если вызван раньше.
- Игнорирование того, что микротаски, добавленные во время выполнения микротасков, тоже обрабатываются в текущем цикле.
- Забывают про рендеринг между циклами event loop.
- В Node.js путают process.nextTick с обычными микротасками (он выполняется раньше).
> Похожие задачи по frontend
В чем разница между Promise.all и Promise.allSettled в JavaScript?
Можно ли изменять массивы и объекты, объявленные через const в JavaScript?
В чем разница между Promise.all, Promise.any и Promise.race в JavaScript
Почему стрелочную функцию нельзя использовать как конструктор в JavaScript
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью