Skip to main content

Мікро- проти макрозадач

Мікрозадачі (microtasks) і макрозадачі (macrotasks) це різні черги задач, які обробляються в різний час усередині Event Loop. Макрозадачі це «великі» події: таймери, мережеві запити, події DOM. Мікрозадачі це «миттєві» задачі, які виконуються одразу після поточного коду, але до наступної макрозадачі.

Теорія

TL;DR

  • Це дві окремі черги: Task Queue для макрозадач і Microtask Queue для мікрозадач.
  • Макрозадачі приходять від зовнішніх API браузера (Web API), мікрозадачі це вбудований механізм рушія JS.
  • За один оберт Event Loop виконується рівно одна макрозадача.
  • Мікрозадачі виконуються всі поспіль, доки їхня черга не спорожніє.
  • Пріоритет мікрозадач вищий: Promise.then завжди випереджає setTimeout(..., 0).
  • Цикл виглядає так: порожній стек, усі мікрозадачі, наступна макрозадача, повтор.

Швидкий приклад

javascript
console.log('1'); setTimeout(() => console.log('2'), 0); Promise.resolve().then(() => console.log('3')); console.log('4');

Порядок виводу:

text
1 4 3 2

Порівняльна таблиця

КритерійМакрозадачі (Task Queue)Мікрозадачі (Microtask Queue)
Коли виконуютьсяПісля очищення стека і всіх мікрозадачОдразу після поточного коду, перед макрозадачею
ЧергаTask QueueMicrotask Queue
ПрикладиsetTimeout, setInterval, setImmediate, обробники подій, XHRPromise.then, Promise.catch, queueMicrotask, MutationObserver
Пріоритетнижчийвищий
Скільки виконується за разОдна за ітерацію Event LoopУсі мікрозадачі поспіль, доки черга не порожня
ДжерелоЗовнішні API браузера (Web API)Вбудовані мікрозадачі рушія JS
Типова метаВідкладене виконання, асинхронні подіїРеакція на завершення промисів, невеликі ланцюжки дій

Розбір прикладу

  1. 1 і 4 виконуються синхронно, у стеку викликів.
  2. Promise.then(...) потрапляє у чергу мікрозадач.
  3. setTimeout(...) потрапляє у чергу макрозадач.
  4. Після очищення стека виконуються всі мікрозадачі, що друкує 3.
  5. Потім виконується перша макрозадача, що друкує 2.

Зверніть увагу: порядок рядків у коді тут не має значення. Навіть якщо оголосити setTimeout першим, промис усе одно спрацює раніше, бо його колбек стоїть в іншій, пріоритетнішій черзі.

Важлива деталь: що робить Event Loop після кожної макрозадачі

Після кожної макрозадачі Event Loop:

  1. виконує всі мікрозадачі, доки їхня черга не спорожніє,
  2. потім бере наступну макрозадачу,
  3. і повторює цикл.

Саме тому мікрозадачі, додані всередині мікрозадачі, теж виконаються в цьому ж оберті, а не в наступному.

Візуально

text
+-----------------------------------------------------+ | Виклики функцій (Call Stack) | |-----------------------------------------------------| | Мікрозадачі (Microtask Queue) | <- одразу після стека |-----------------------------------------------------| | Макрозадачі (Task Queue) | <- після мікрозадач +-----------------------------------------------------+

Коротко про терміни

ЩоКолиПриклад
МікрозадачаПісля поточного коду, до макрозадачPromise.then, queueMicrotask
МакрозадачаПісля всіх мікрозадачsetTimeout, setInterval, події DOM
Event LoopСтежить за порядком: порожній стек, мікрозадачі, макрозадача, повторціле керування циклом

Типові помилки

  • Вважати, що черговість визначається порядком рядків у коді. Її визначає черга, у яку потрапив колбек.
  • Думати, що setTimeout(..., 0) це «виконати негайно». Це найраніша можлива макрозадача, тобто після всіх мікрозадач.
  • Плутати сам fetch із його then. Мережева відповідь приходить через Web API, а ось колбек then це вже мікрозадача.
  • Нескінченно породжувати мікрозадачі. Черга ніколи не спорожніє, і макрозадачі разом з рендерингом не отримають керування.
  • Розраховувати на однаковий порядок у браузері і в Node.js. Набір джерел макрозадач відрізняється, наприклад setImmediate є лише в Node.js.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.