Основные понятия
В JavaScript асинхронные операции делятся на два типа задач, которые обрабатываются Event Loop по разным правилам:
1. Макротаски
- Выполняются после текущего синхронного кода и всех микротасок
- Помещаются в Task Queue (Callback Queue)
- Типичные примеры:
setTimeout()
setInterval()
setImmediate()
(Node.js)
- События DOM (click, load и др.)
- I/O операции
- UI rendering
2. Микротаски
- Выполняются сразу после текущего синхронного кода, но перед макротасками
- Помещаются в Microtask Queue
- Типичные примеры:
Promise.then()
/catch()
/finally()
queueMicrotask()
MutationObserver
process.nextTick()
(Node.js)
Детальный механизм работы
Event Loop обрабатывает задачи в строгом порядке:
- Выполняет весь синхронный код (Call Stack)
- Обрабатывает все доступные микротаски
- Выполняет одну макротаску
- Повторяет цикл
Наглядный пример:
console.log('Script start');
setTimeout(() => {
console.log('setTimeout');
}, 0);
Promise.resolve()
.then(() => {
console.log('Promise 1');
})
.then(() => {
console.log('Promise 2');
});
console.log('Script end');
Вывод:
- Script start
- Script end
- Promise 1
- Promise 2
- setTimeout
Особенности в Angular
Angular использует зону (NgZone) для отслеживания асинхронных операций и запуска Change Detection:
-
Микротаски:
- Promise-обработчики
- Вызовы
async
/await
- Вызывают Change Detection сразу после выполнения
-
Макротаски:
- setTimeout/setInterval
- DOM события
- Также вызывают Change Detection
Пример Angular-специфичного поведения:
ngOnInit() {
setTimeout(() => {
this.value = 'Macrotask'; // Вызовет Change Detection
}, 0);
Promise.resolve().then(() => {
this.value = 'Microtask'; // Вызовет Change Detection раньше
});
}
Опасные ситуации
- Рекурсия микротасок:
function recursiveMicrotask() {
Promise.resolve().then(recursiveMicrotask);
}
Блокирует основной поток, так как Event Loop не перейдет к макротаскам.
- Долгие вычисления в микротасках:
- Могут задержать критически важные макротаски (например, анимации)
Практические рекомендации
- Для фоновых задач используйте макротаски (
setTimeout
)
- Для высокоприоритетных операций - микротаски (
queueMicrotask
)
- В Angular для оптимизации:
NgZone.runOutsideAngular()
+ requestAnimationFrame()
для анимаций
ChangeDetectorRef.detectChanges()
для ручного управления
Резюмируем
-
Микротаски:
- Более высокий приоритет
- Выполняются до рендеринга и макротасок
- Используются для Promise, MutationObserver
-
Макротаски:
- Более низкий приоритет
- Включают таймеры, события, I/O
- Выполняются после всех микротасок
-
В Angular:
- Оба типа задач запускают Change Detection
- Микротаски могут привести к частым проверкам
- Оптимизация требует понимания их работы
Понимание разницы критически важно для:
- Оптимального управления Change Detection
- Предотвращения проблем с производительностью
- Создания отзывчивых интерфейсов