Event loop для чайников: простыми словами о сложном механизме браузера
Event Loop — это сердце асинхронности в JavaScript. В этой статье простыми словами разберем, как работает цикл событий в браузере, что такое макрозадачи и микрозадачи, и как они влияют на выполнение кода. С примерами, схемами и лайфхаками для лучшего понимания. Идеально для тех, кто хочет разобраться в сложном механизме без лишней воды!
314 открытий2К показов

Салют, народ! 👋
Это моя первая проба пера, так что не судите строго. Сегодня мы разберем одну из самых интересных и, на первый взгляд, сложных тем в JavaScript — Event Loop (или цикл событий). Если вы когда-нибудь задумывались, как браузер умудряется выполнять множество задач одновременно, не зависая при этом, то вы попали по адресу.
Event Loop — механизм, который управляет асинхронными операциями в JavaScript. Он позволяет обрабатывать задачи, не блокируя основной поток выполнения программы. Это особенно важно для создания отзывчивых интерфейсов пользователя и эффективной работы серверов.
Интересно, что Event Loop работает как в браузере, так и в Node.js, но с некоторыми отличиями. В этой статье мы сосредоточимся на том, как это происходит в браузере, и постараемся объяснить всё максимально просто, даже если вы только начинаете свой путь в программировании.
Поехали! 🚀
Как работает Event Loop и из чего состоит
Структура Event Loop
Основная цель Event Loop — циклическое выполнение задач, которые делятся на две категории:
- Синхронные задачи — выполняются немедленно;
- Асинхронные задачи — добавляются в очередь и выполняются позже.
Макрозадачи и микрозадачи
Одним из ключевых аспектов работы event loop выступают макрозадачи (macrotasks) и микрозадачи (microtasks). Они представляют собой две очереди задач, которые имеют различный приоритет выполнения.
- Макрозадачи. Выполняются после завершения текущей синхронной задачи. Примеры:Синхронный код; таймеры (
setTimeout
,setInterval
); события пользовательского интерфейса (например, клики мыши); сетевые запросы (например,fetch
или XMLHttpRequest). - Микрозадачи. Выполняются сразу после завершения текущей синхронной задачи, но перед началом следующей макрозадачи. Примеры:Промисы (
Promise.resolve()
,Promise.reject()
).MutationObserver
.async/await).
В Node.js также есть event loop, но с небольшими различиями. Например, там используются дополнительные очереди для работы с файловой системой и сетью.
Схема приоритетов выполнения:
- Сначала выполняется текущий синхронный код;
- Затем выполняются все микрозадачи в очереди Microtask queue до тех пор, пока очередь не станет пустой;
- После этого выполняется следующая макрозадача из очереди Macrotask queue и т.д.
Как поведет себя Event Loop в реальной жизни: разбираем код
Схемы это конечно хорошо, но всегда лучше изучать на примерах кода. Далее посмотрим куски, которые могут встретиться во время работы или на собеседованиях.
Вложенные промисы и микрозадачи
Порядок вывода:
Start
First promise
Second promise
Timeout 1
Promise in timeout
Объяснение:
- Сначала выполняется синхронный код (
console.log('Start')
). - Затем выполняются микрозадачи (микротаски) — это цепочки промисов.
- После завершения всех микрозадач выполняются макрозадачи (например,
setTimeout
).
Асинхронные функции и генераторы
Порядок вывода:
Async start
Main thread
Generator timeout
After generator
Объяснение:
- Функция
asyncFunction
начинает выполнение, но останавливается наawait
, пока не завершится промис, созданный генератором. - Синхронный код продолжает выполняться (
console.log('Main thread')
). - Когда таймер завершается, event loop обрабатывает завершение промиса и продолжает выполнение
asyncFunction
.
Взаимодействие с DOM и таймерами
Порядок вывода:
Initial timeout
Button clicked
Timeout after click
Объяснение:
- Первый таймер добавляет событие клика на кнопку.
- После выполнения первого таймера происходит имитация клика на кнопку.
- Event loop обрабатывает событие клика и устанавливает второй таймер.
- После завершения всех синхронных задач выполняются все установленные таймеры.
Рекурсивные промисы и рекурсия
Порядок вывода:
Outside recursion
Recursive 3
Recursive 2
Recursive 1
Объяснение:
- Вызов
recursivePromise(3)
запускает цепочку промисов и таймеров. - Синхронный код (
console.log('Outside recursion')
) выполняется первым. - Event loop последовательно обрабатывает каждый вызов рекурсии, начиная с самого глубокого уровня.
Параллельные сетевые запросы и обработка результатов
Порядок вывода:
Starting fetches
Fetched data from ...
(в случайном порядке)All data fetched: [...]
Объяснение:
- Все сетевые запросы начинаются параллельно, но завершаются в разное время из-за случайной задержки.
- Event loop обрабатывает завершение каждого запроса по мере их выполнения.
- После завершения всех запросов выполняется обработчик
Promise.all
.
Эти примеры показывают, как event loop управляет асинхронными операциями и как важно понимать порядок выполнения кода для правильного написания асинхронных приложений.
Лайфхаки для понимания Event Loop
Используйте визуализацию
Используйте инструменты вроде Chrome DevTools или онлайн-симуляторов Event Loop, чтобы видеть порядок выполнения задач.
Создайте шаблоны
Создайте шаблонные ситуации и попробуйте изменять их, чтобы лучше понять влияние различных элементов.
Регулярно практикуйтесь
Регулярно решайте задачи и пишите код, связанный с асинхронностью, чтобы закрепить знания.
Изучите документацию
Изучите официальную документацию по JavaScript и спецификации ECMAScript для более глубокого понимания.
Сегодня мы получили базовое представление о том, как работает Event Loop, его особенности и возможные подводные камни. Практикуйтесь и экспериментируйте, чтобы лучше понять этот важный механизм.
314 открытий2К показов