В какой очередности выполняются микротаски и макротаски
Комментарии (1)
Ответ сгенерирован нейросетью и может содержать ошибки
Очередность выполнения микротаск (Microtasks) и макротаск (Macrotasks)
Микротаски и макротаски — это концепция, которая лежит в основе асинхронности в JavaScript. Понимание их порядка выполнения критично для прогнозирования поведения асинхронного кода.
Что такое макротаски (Macrotasks)?
Макротаски — это обычные асинхронные операции, которые выполняются браузером в отдельной очереди. К ним относятся:
setTimeoutsetIntervalsetImmediate(в Node.js)requestAnimationFrame- I/O операции
- Рендеринг (отрисовка)
- Обработка событий (click, input и т.д.)
Что такое микротаски (Microtasks)?
Микротаски — это более приоритетные асинхронные операции, которые выполняются ДО переходов к следующей макротаске. К ним относятся:
Promise.then(),Promise.catch(),Promise.finally()async/await(это синтаксис сахара для Promise)MutationObserverqueueMicrotask()
Очередность выполнения (Event Loop)
JavaScript использует Event Loop для управления выполнением кода. Вот порядок:
1. Выполни синхронный код
2. Выполни все микротаски (полностью очистить микротаску очередь)
3. Выполни одну макротаску
4. Выполни все микротаски (снова очистить полностью)
5. Повтори шаги 3-4
Пример для понимания
console.log('1. Синхронный код: НАЧАЛО');
setTimeout(() => {
console.log('5. Макротаска: setTimeout');
}, 0);
Promise.resolve()
.then(() => {
console.log('3. Микротаска: Promise 1');
})
.then(() => {
console.log('4. Микротаска: Promise 2');
});
console.log('2. Синхронный код: КОНЕЦ');
/* Вывод:
1. Синхронный код: НАЧАЛО
2. Синхронный код: КОНЕЦ
3. Микротаска: Promise 1
4. Микротаска: Promise 2
5. Макротаска: setTimeout
*/
Почему микротаски выполняются раньше макротасок?
Browsers и Node.js обрабатывают микротаски с более высоким приоритетом:
console.log('1. Start');
setTimeout(() => {
console.log('5. setTimeout');
Promise.resolve().then(() => {
console.log('6. Promise inside setTimeout');
});
}, 0);
Promise.resolve()
.then(() => {
console.log('2. Promise 1');
setTimeout(() => {
console.log('7. setTimeout inside Promise');
}, 0);
})
.then(() => {
console.log('3. Promise 2');
});
console.log('4. End');
/* Вывод:
1. Start
4. End
2. Promise 1
3. Promise 2
5. setTimeout
6. Promise inside setTimeout
7. setTimeout inside Promise
*/
Сложный пример с несколькими макротасками
console.log('1. Скрипт');
setTimeout(() => {
console.log('4. setTimeout 1');
Promise.resolve().then(() => {
console.log('5. Promise в setTimeout 1');
});
}, 0);
setTimeout(() => {
console.log('7. setTimeout 2');
}, 0);
Promise.resolve()
.then(() => {
console.log('2. Promise 1');
setTimeout(() => {
console.log('8. setTimeout в Promise');
}, 0);
})
.then(() => {
console.log('3. Promise 2');
});
console.log('6. Конец скрипта');
/* Вывод:
1. Скрипт
6. Конец скрипта
2. Promise 1
3. Promise 2
4. setTimeout 1
5. Promise в setTimeout 1
7. setTimeout 2
8. setTimeout в Promise
*/
Жизненный цикл Event Loop (подробный)
- Call Stack (стек вызовов) выполняет синхронный код
- Когда стек пуст:
- Проверяется Microtask Queue
- Все микротаски выполняются полностью
- Затем выполняется одна задача из Macrotask Queue
- Проверяется Microtask Queue снова
- Рендеринг (если нужен)
- Повторить с шага 2
┌─────────────────────────────────────────┐
│ Call Stack (синхронный код) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Microtask Queue (Promises, async/await) │
└─────────────────────────────────────────┘
↓
┌─────────────────────────────────────────┐
│ Macrotask Queue (setTimeout, events) │
└─────────────────────────────────────────┘
↓
Рендеринг
↓
Повтор
async/await также использует микротаски
async function main() {
console.log('1. Start');
await Promise.resolve();
console.log('2. After await'); // Это микротаска!
}
main();
setTimeout(() => {
console.log('3. setTimeout');
}, 0);
/* Вывод:
1. Start
2. After await
3. setTimeout
*/
Практическое применение
Проблема: частая ошибка — ожидание того, что setTimeout выполнится раньше Promise:
// ❌ Ошибочное предположение
setTimeout(() => {
console.log('Это выполнится раньше!');
}, 0);
Promise.resolve().then(() => {
console.log('Это выполнится позже...');
});
// На самом деле Promise выполнится раньше!
Решение: помни приоритет:
- Promises (микротаски) имеют приоритет выше
- setTimeout (макротаски) выполняются после
Резюме
Порядок выполнения:
- Синхронный код (Call Stack)
- Все микротаски полностью (Promises, async/await)
- Одна макротаска (setTimeout, события)
- Все микротаски снова
- Рендеринг
- Повторить с пункта 3
Запомни: Микротаски выполняются РАНЬШЕ макротасок. Это основное правило Event Loop в JavaScript.