Что попадает к Микрозадачам кроме Promise?
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Микрозадачи в JavaScript: не только Promise
В современном JavaScript Event Loop управляет выполнением кода, используя очереди задач. Микрозадачи (microtasks) — это особый тип задач с высшим приоритетом, которые выполняются после завершения текущей синхронной задачи, но до рендеринга и выполнения макрозадач. Хотя Promise действительно являются самым распространённым источником микрозадач, ими список не ограничивается.
Основные источники микрозадач
1. Promise
Методы Promise создают микрозадачи для своих обработчиков:
Promise.then()/Promise.catch()/Promise.finally()Promise.resolve()/Promise.reject()(при цепочке вызовов)async/await(синтаксический сахар над Promise)
Promise.resolve().then(() => console.log('Микрозадача от Promise'));
2. MutationObserver
API для наблюдения за изменениями в DOM. Коллбэки MutationObserver выполняются как микрозадачи.
const observer = new MutationObserver(() => {
console.log('Микрозадача от MutationObserver');
});
observer.observe(document.body, { childList: true });
3. queueMicrotask()
Специальный API, добавленный для явного помещения функций в очередь микрозадач.
queueMicrotask(() => {
console.log('Явно добавленная микрозадача');
});
4. process.nextTick() (только Node.js)
В среде Node.js process.nextTick() создаёт микрозадачи с даже более высоким приоритетом, чем Promise.
process.nextTick(() => {
console.log('Микрозадача в Node.js');
});
5. Object.observe() (устаревший)
Устаревший API, который также использовал микрозадачи для своих коллбэков.
Практическое значение понимания микрозадач
Порядок выполнения
Микрозадачи образуют очередь (FIFO), которая опустошается полностью перед переходом к следующей макрозадаче:
console.log('1. Синхронный код');
setTimeout(() => console.log('6. Макрозадача (setTimeout)'), 0);
Promise.resolve()
.then(() => {
console.log('3. Микрозадача 1 от Promise');
return 'result';
})
.then((value) => {
console.log('4. Микрозадача 2 от Promise:', value);
});
queueMicrotask(() => console.log('5. Микрозадача от queueMicrotask'));
console.log('2. Конец синхронного кода');
Важные последствия для производительности
-
Рекурсивные микрозадачи могут заблокировать Event Loop
function recursiveMicrotask() { Promise.resolve().then(recursiveMicrotask); } // Вызов заблокирует основной поток навсегда! -
Порядок рендеринга: браузер рендерит изменения только после полного опустошения очереди микрозадач.
Отличия от макрозадач
| Микрозадачи | Макрозадачи |
|---|---|
| Promise, queueMicrotask | setTimeout, setInterval |
| MutationObserver | setImmediate (Node.js) |
| Высший приоритет | Низший приоритет |
| Выполняются между макрозадачами | Выполняются в отдельных тиках Event Loop |
Паттерны использования
Пакетирование операций
let isPending = false;
let queue = [];
function processQueue() {
if (isPending) return;
isPending = true;
queueMicrotask(() => {
// Обработка всех накопленных операций
while (queue.length) {
const task = queue.shift();
task();
}
isPending = false;
});
}
Избегание "залипания" UI
// Плохо - блокирует рендеринг
function processData(data) {
data.forEach(item => heavyCalculation(item));
}
// Хорошо - позволяет браузеру рендерить
async function processData(data) {
for (const item of data) {
await Promise.resolve(); // Разрешает рендеринг между операциями
heavyCalculation(item);
}
}
Особенности браузеров и Node.js
В браузерах порядок обычно такой:
- Выполнение синхронного кода
- Опустошение очереди микрозадач
- Рендеринг (при необходимости)
- Выполнение одной макрозадачи
- Повтор цикла
В Node.js ситуация сложнее из-за дополнительных фаз Event Loop (timers, poll, check и т.д.), но принцип приоритета микрозадач сохраняется.
Заключение
Понимание микрозадач выходит за рамки работы с Promise и является фундаментальным аспектом асинхронного программирования в JavaScript. Правильное использование различных источников микрозадач позволяет оптимизировать производительность, управлять приоритетом операций и создавать отзывчивые интерфейсы. Современные API вроде queueMicrotask() дают разработчикам прямой контроль над этим механизмом, что особенно полезно при реализации сложных асинхронных паттернов и библиотек.