Мікро- проти макрозадач
Мікрозадачі (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 Queue | Microtask Queue |
| Приклади | setTimeout, setInterval, setImmediate, обробники подій, XHR | Promise.then, Promise.catch, queueMicrotask, MutationObserver |
| Пріоритет | нижчий | вищий |
| Скільки виконується за раз | Одна за ітерацію Event Loop | Усі мікрозадачі поспіль, доки черга не порожня |
| Джерело | Зовнішні API браузера (Web API) | Вбудовані мікрозадачі рушія JS |
| Типова мета | Відкладене виконання, асинхронні події | Реакція на завершення промисів, невеликі ланцюжки дій |
Розбір прикладу
1і4виконуються синхронно, у стеку викликів.Promise.then(...)потрапляє у чергу мікрозадач.setTimeout(...)потрапляє у чергу макрозадач.- Після очищення стека виконуються всі мікрозадачі, що друкує
3. - Потім виконується перша макрозадача, що друкує
2.
Зверніть увагу: порядок рядків у коді тут не має значення. Навіть якщо оголосити setTimeout першим, промис усе одно спрацює раніше, бо його колбек стоїть в іншій, пріоритетнішій черзі.
Важлива деталь: що робить Event Loop після кожної макрозадачі
Після кожної макрозадачі Event Loop:
- виконує всі мікрозадачі, доки їхня черга не спорожніє,
- потім бере наступну макрозадачу,
- і повторює цикл.
Саме тому мікрозадачі, додані всередині мікрозадачі, теж виконаються в цьому ж оберті, а не в наступному.
Візуально
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
Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.