Что такое Event Loop
Event Loop — это механизм в JavaScript, который позволяет выполнять асинхронный код в однопоточной модели, управляя очередями задач и обеспечивая их выполнение, когда основной поток освобождается. Он следит за порядком выполнения синхронного кода, микрозадач и макрозадач, что критично для понимания асинхронности в браузере и Node.js.
Как это работает
JavaScript выполняется в одном потоке, поэтому для обработки асинхронных операций (таймеры, запросы, события) используется Event Loop. Его работа состоит из нескольких этапов:
- Синхронный код: все вызовы попадают в Call Stack и выполняются последовательно.
- Асинхронные операции: когда встречается асинхронная функция (например,
setTimeoutилиfetch), она передается в Web APIs (в браузере) или в libuv (в Node.js), а колбэк регистрируется для последующего выполнения. - Очистка стека: когда Call Stack пуст, Event Loop обрабатывает очереди задач.
- Повторение цикла: процесс продолжается, пока есть задачи.
Очереди задач
Event Loop использует две основные очереди:
- Микрозадачи (Microtasks): сюда попадают колбэки промисов (
.then(),.catch(),.finally()),queueMicrotask, а такжеMutationObserver. Микрозадачи имеют приоритет и выполняются сразу после завершения текущего синхронного кода, но перед макрозадачами. - Макрозадачи (Macrotasks): включают
setTimeout,setInterval,setImmediate, события (например,onclick), а также I/O-операции. Они выполняются после того, как все микрозадачи обработаны.
Пример
Рассмотрим порядок выполнения в следующем коде:
console.log('Синхронный код');
setTimeout(() => {
console.log('Макрозадача');
}, 0);
Promise.resolve().then(() => {
console.log('Микрозадача');
});
console.log('Конец синхронного кода');Вывод будет:
Синхронный код
Конец синхронного кода
Микрозадача
МакрозадачаОбъяснение: сначала выполняется весь синхронный код, затем Event Loop обрабатывает очередь микрозадач (промис), и только потом — макрозадачу (setTimeout).
Подводные камни
- Блокировка потока: если в микрозадаче запустить бесконечный цикл, Event Loop не перейдет к макрозадачам, и страница зависнет.
- Порядок таймеров:
setTimeoutс задержкой 0 не гарантирует мгновенное выполнение — он ждет, пока стек освободится и все микрозадачи завершатся. - Различия окружений: в Node.js есть
process.nextTick, который выполняется до микрозадач, аsetImmediate— после макрозадач, что может отличаться от браузерного поведения.
Когда использовать
Понимание Event Loop необходимо для:
- Оптимизации производительности: избегание блокировки основного потока тяжелыми вычислениями.
- Работы с асинхронными API: правильное использование промисов,
async/awaitи таймеров. - Отладки неочевидного порядка выполнения кода.
Коротко
- Event Loop — это цикл, который управляет выполнением синхронного кода, микрозадач и макрозадач в одном потоке.
- Микрозадачи (промисы,
queueMicrotask) выполняются раньше макрозадач (setTimeout, события). - Call Stack должен быть пуст, прежде чем Event Loop начнет обрабатывать очереди.
- Знание порядка выполнения помогает избегать ошибок и писать эффективный асинхронный код.
