Расскажи про event loop
Event Loop (цикл событий) — это механизм в JavaScript, который позволяет выполнять асинхронный код в однопоточном окружении, не блокируя основной поток. Он непрерывно проверяет стек вызовов и очередь задач, перемещая колбэки из очереди в стек, когда тот освобождается. Благодаря этому JavaScript может обрабатывать таймеры, сетевые запросы и события пользовательского интерфейса, сохраняя отзывчивость приложения.
Как это работает
JavaScript работает в одном потоке, то есть в каждый момент времени выполняется только одна операция. Если бы не было Event Loop, любая длительная операция (например, запрос к серверу) блокировала бы выполнение всего кода, замораживая интерфейс. Event Loop решает эту проблему, используя три ключевые структуры:
- Call Stack (стек вызовов) — хранит текущий контекст выполнения. Когда вызывается функция, она добавляется в стек; когда функция завершается, она удаляется из стека.
- Callback Queue (очередь обратных вызовов) — сюда попадают колбэки от асинхронных операций после их завершения.
- Event Loop — постоянно проверяет, пуст ли стек вызовов. Если стек пуст, он берёт первый колбэк из очереди и помещает его в стек для выполнения.
Этот процесс повторяется бесконечно, что и создаёт цикл событий.
Пример
Рассмотрим простой пример с setTimeout:
console.log('Первое сообщение');
setTimeout(() => {
console.log('Сообщение из setTimeout');
}, 0);
console.log('Второе сообщение');Несмотря на задержку в 0 миллисекунд, вывод будет таким:
Первое сообщение
Второе сообщение
Сообщение из setTimeoutПочему? Потому что setTimeout помещает свой колбэк в очередь обратных вызовов, а не выполняет его сразу. Event Loop сначала завершает выполнение всего синхронного кода в стеке (включая console.log('Второе сообщение')), и только когда стек становится пустым, колбэк из очереди попадает в стек и выполняется.
Подводные камни
- Очередь микрозадач: В дополнение к обычной очереди обратных вызовов существует очередь микрозадач (например, промисы). Микрозадачи имеют приоритет над обычными колбэками: Event Loop обрабатывает все микрозадачи из очереди перед тем, как взять следующую задачу из callback queue. Это может привести к неожиданному порядку выполнения, если не учитывать этот нюанс.
- Блокировка стека: Если в стеке вызовов выполняется тяжёлая синхронная операция (например, бесконечный цикл), Event Loop не сможет обработать очередь, и приложение «зависнет». Поэтому важно не перегружать основной поток.
- setTimeout с задержкой 0 не гарантирует мгновенное выполнение — он лишь ставит колбэк в очередь, и время выполнения зависит от текущей загрузки стека.
Когда использовать
Понимание Event Loop необходимо при работе с асинхронным кодом: обработке событий, таймерах, промисах, async/await, запросах к серверу. Это помогает избежать ошибок, связанных с порядком выполнения, и писать эффективный неблокирующий код.
Коротко
- Event Loop позволяет JavaScript выполнять асинхронные операции в однопоточном режиме, не блокируя основной поток.
- Основные компоненты: стек вызовов, очередь обратных вызовов и сам цикл, который переносит колбэки из очереди в стек.
- Порядок выполнения: сначала весь синхронный код, затем колбэки из очереди; микрозадачи (промисы) выполняются раньше обычных колбэков.
- Избегайте тяжёлых синхронных операций в основном потоке, чтобы не блокировать Event Loop.
