Skip to main content

Мікрозадачі (microtasks)

Мікрозадачі (microtasks) це невеликі задачі, які виконуються одразу після поточного коду (поточної макрозадачі) і до того, як почнеться наступна макрозадача з черги. Вони утворюють окрему структуру, чергу мікрозадач (Microtask Queue).

Теорія

TL;DR

  • Мікрозадача це колбек, який Event Loop виконує після поточного коду, але раніше за будь-яку макрозадачу.
  • Основні джерела: Promise.then, Promise.catch, queueMicrotask, MutationObserver.
  • Черга мікрозадач вичерпується повністю, а з черги макрозадач за оберт береться лише одна задача.
  • Пріоритет мікрозадач вищий, тому Promise.then завжди випереджає setTimeout(..., 0).
  • Нескінченне породження мікрозадач «заморожує» Event Loop: макрозадачі і рендеринг не отримають керування.
  • Порядок: стек, усі мікрозадачі, наступна макрозадача.

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

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

Підсумковий порядок виводу:

text
1 4 3 2

Ключова ідея

У JavaScript є два рівні задач:

ТипПрикладиКоли виконується
Макрозадачі (Tasks)setTimeout, setInterval, події DOM, мережеві колбекиПісля очищення стека
Мікрозадачі (Microtasks)Promise.then, Promise.catch, queueMicrotask, MutationObserverОдразу після поточної задачі, до наступної макрозадачі

Тобто після виконання поточного коду (наприклад, функції) Event Loop спершу перевіряє чергу мікрозадач. Якщо в ній є задачі, вони виконуються всі поспіль, доки черга не спорожніє.

Покроковий розбір прикладу

  1. console.log('1') виконується одразу. Вивід: 1
  2. setTimeout(..., 0): колбек потрапляє у чергу макрозадач (task queue).
  3. Promise.then(...): колбек потрапляє у чергу мікрозадач.
  4. console.log('4') виконується одразу. Вивід: 4
  5. Поточний стек порожній, тому виконуються всі мікрозадачі, тобто console.log('3').
  6. І лише після цього виконуються макрозадачі, тобто console.log('2').

Що створює мікрозадачі

ДжерелоПриклад
PromisePromise.resolve().then(...)
queueMicrotask()queueMicrotask(() => { ... })
MutationObserverВідстеження змін DOM

Сюди ж належить і async/await: продовження після await планується як мікрозадача.

Відмінність мікрозадач від макрозадач

ВластивістьМікрозадачіМакрозадачі
ЧергаMicrotask QueueTask Queue
ПрикладиPromise.then, queueMicrotasksetTimeout, setInterval, click
ПріоритетВищий, виконуються ранішеНижчий
Коли виконуютьсяПісля поточного коду, але до наступної подіїПісля всіх мікрозадач

Обережно з мікрозадачами

Якщо нескінченно додавати нові мікрозадачі всередині then, рушій ніколи не дійде до наступної макрозадачі, і вийде «заморожений» Event Loop:

javascript
function loop() { Promise.resolve().then(loop); } loop(); // браузер зависне

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

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

ТермінОпис
MicrotaskМаленька задача, що виконується після поточного коду і до наступної макрозадачі
Черга мікрозадачЗберігає колбеки Promise.then, queueMicrotask і подібні
ПріоритетВищий, ніж у макрозадач
Порядок Event LoopСтек, потім усі мікрозадачі, потім наступна макрозадача

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

  • Ставити Promise.then і setTimeout(..., 0) в один ряд. Промис це мікрозадача, і він виконається раніше завжди, незалежно від порядку в коді.
  • Вважати, що мікрозадача це «швидший таймер». Це інша черга з іншим пріоритетом, а не менша затримка.
  • Рекурсивно планувати мікрозадачі для фонової роботи. Так ви блокуєте рендеринг; для порційної роботи беріть setTimeout або requestIdleCallback.
  • Забувати, що продовження після await теж мікрозадача. Через це код після await виконується пізніше, ніж очікують, але все одно раніше за таймери.
  • Плутати чергу мікрозадач зі стеком викликів. Мікрозадачі виконуються лише тоді, коли стек уже порожній.

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

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

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