Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Что такое микрозадача (Microtask)?
Микрозадача (microtask) — это задача, которая выполняется в JavaScript с более высоким приоритетом, чем обычные задачи (макрозадачи/macrotasks). Понимание микрозадач критически важно для овладения асинхронным JavaScript и event loop.
Event Loop — полная картина
JavaScript Event Loop имеет три очереди:
┌─────────────────┐
│ Call Stack │ Выполняет синхронный код
└─────────────────┘
↓
┌─────────────────────────────────┐
│ Microtask Queue (приоритет 1) │ ← Выполняется ПЕРВОЙ
│ - Promise.then/catch/finally │
│ - async/await │
│ - queueMicrotask() │
│ - MutationObserver │
└─────────────────────────────────┘
↓
┌─────────────────────────────────┐
│ Macrotask Queue (приоритет 2) │ ← Выполняется ПОСЛЕ microtask
│ - setTimeout/setInterval │
│ - setImmediate │
│ - I/O операции │
│ - UI рендеринг │
└─────────────────────────────────┘
Порядок выполнения
- Выполни все синхронные операции (Call Stack)
- Опусти Call Stack
- Выполни ВСЕ микрозадачи из очереди
- Выполни ОДНУ макрозадачу
- Вернись к шагу 3
Примеры микрозадач
Promise callbacks:
console.log('1');
Promise.resolve()
.then(() => console.log('2'))
.then(() => console.log('3'));
console.log('4');
// Вывод:
// 1
// 4
// 2
// 3
// Объяснение:
// 1. console.log('1') — синхронно выведет сразу
// 2. Promise.then() добавляется в микроочередь
// 3. console.log('4') — синхронно выведет сразу
// 4. Call Stack опустошен запускаем микротаски
// 5. Выполняется .then(() => console.log('2')), добавляется новый .then()
// 6. Выполняется .then(() => console.log('3'))
async/await (это Promise под капотом):
console.log('1');
async function myFunc() {
console.log('2');
await Promise.resolve();
console.log('3');
}
myFunc();
console.log('4');
// Вывод:
// 1
// 2
// 4
// 3
// Объяснение:
// await эквивалентен .then(), добавляет задачу в микроочередь
Макрозадачи vs Микрозадачи
console.log('Синхронно 1');
setTimeout(() => {
console.log('Макротаск (setTimeout)');
}, 0);
Promise.resolve()
.then(() => console.log('Микротаск (Promise)'));
console.log('Синхронно 2');
// Вывод:
// Синхронно 1
// Синхронно 2
// Микротаск (Promise)
// Макротаск (setTimeout)
// Даже с setTimeout 0, Promise выполнится РАНЬШЕ!
Практическое применение
Опасный паттерн — надежда на порядок выполнения:
// Ненадежно
let data = null;
fetch('/api/data')
.then(r => r.json())
.then(result => { data = result; });
console.log(data); // null! Fetch еще не завершился
Правильно — использовать асинхронность правильно:
// Правильно
async function getData() {
const response = await fetch('/api/data');
const data = await response.json();
return data;
}
getData().then(data => {
console.log(data); // Теперь данные точно готовы
});
queueMicrotask() — явное добавление микрозадачи
console.log('Start');
queueMicrotask(() => {
console.log('Microtask');
});
console.log('End');
// Вывод:
// Start
// End
// Microtask
// Это эквивалентно:
Promise.resolve().then(() => console.log('Microtask'));
Сложный пример с множественными очередями
console.log('Start');
setTimeout(() => {
console.log('setTimeout 1');
Promise.resolve().then(() => console.log('Promise inside setTimeout'));
}, 0);
Promise.resolve()
.then(() => {
console.log('Promise 1');
setTimeout(() => console.log('setTimeout inside Promise'), 0);
})
.then(() => console.log('Promise 2'));
console.log('End');
// Вывод:
// Start
// End
// Promise 1
// Promise 2
// setTimeout 1
// Promise inside setTimeout
// setTimeout inside Promise
Почему микрозадачи нужны?
Гарантия порядка — все микрозадачи выполняются до следующей макротаски:
// Promise.then() должен выполниться ДО setTimeout()
// Это критично для консистентности
Promise.resolve().then(() => {
// Это выполнится перед любым setTimeout
});
setTimeout(() => {
// Это выполнится после всех Promise
});
MutationObserver — микрозадача
const observer = new MutationObserver(() => {
console.log('DOM изменился');
});
const element = document.getElementById('target');
observer.observe(element, { childList: true });
console.log('Синхронно');
element.innerHTML = '<p>New content</p>';
console.log('После изменения');
// Вывод:
// Синхронно
// После изменения
// DOM изменился (микротаск выполнится после синхронного кода)
Заключение
Микрозадачи — это фундаментальная часть JavaScript Event Loop. Понимание их порядка выполнения относительно макрозадач критически важно для написания предсказуемого асинхронного кода. Главное правило: все микрозадачи выполняются до следующей макротаски, что обеспечивает более предсказуемое и стабильное поведение асинхронного кода.