JavaRush /Курсы /Модуль 4: Node.js, Next.js и Angular /Event Loop: базовая схема работы

Event Loop: базовая схема работы

Модуль 4: Node.js, Next.js и Angular
2 уровень , 0 лекция
Открыта

1. Что такое Event Loop?

Давайте представим: вы — официант в ресторане. В большинстве языков программирования (например, в Java или Python без async) официант принимает заказ, уходит на кухню, ждёт, пока повар приготовит блюдо, приносит его клиенту и только потом подходит к следующему столу. Если повар медленный — беда: остальные клиенты ждут, официант скучает на кухне.

Node.js действует иначе. Официант в Node.js принимает заказ, тут же передаёт его повару (или бариста, или кому угодно) и возвращается к обслуживанию следующего клиента. Как только блюдо готово — повар даёт знать, и официант приносит его клиенту. Никто не простаивает, клиенты довольны, бизнес процветает.

Вот эта система «делегирования» и есть Event Loop: механизм, который позволяет JavaScript не блокировать выполнение программы, а обрабатывать множество задач одновременно (ну, почти одновременно — технически, по очереди, но очень быстро).

Event Loop — это "сердце" JavaScript-движка (и в браузере, и в Node.js), отвечающее за выполнение кода, обработку событий и выполнение асинхронных операций.

В Node.js Event Loop позволяет:

  • не блокировать выполнение кода при долгих операциях (например, чтение файла, запрос в Интернет);
  • запускать обработчики событий, когда что-то произошло (например, пришёл HTTP-запрос или завершился таймер);
  • делать вид, что всё происходит одновременно, хотя на самом деле JavaScript исполняется в одном потоке.

Почему это важно?

Node.js идеально подходит для серверов, где тысячи клиентов могут одновременно отправлять запросы, а сервер не «зависает» на каждом из них, а ловко жонглирует задачами благодаря Event Loop.

2. Однопоточность и асинхронность: в чём подвох?

Многие новички удивляются: «JavaScript однопоточный, но как он умудряется делать несколько вещей сразу?»

  • Однопоточность: Вся программа JavaScript (и в браузере, и в Node.js) исполняется в одном потоке. Это значит, что в каждый момент времени выполняется только одна строка кода.
  • Асинхронность: Благодаря Event Loop и специальным механизмам (колбэки, промисы, async/await), JavaScript может «откладывать» выполнение долгих задач и возвращаться к ним позже, не блокируя основной поток.

Аналогия

Представьте, что у вас есть только одна рука (один поток), но вы умеете очень быстро делать пометки на стикерах: «Когда закипит чайник — напомнить себе налить чай». Пока чайник греется, вы делаете другие дела. Как только чайник закипел, кто-то (таймер) говорит вам: «Эй, пора наливать!» — и вы тут же реагируете.

3. Как работает Event Loop: пошагово

Основные участники

  • Call Stack (стек вызовов): Здесь исполняется весь ваш код. Каждый раз, когда вызывается функция, она помещается в стек. Как только функция завершается — убирается из стека.
  • Callback Queue (очередь колбэков): Сюда попадают функции-обработчики, которые должны выполниться, когда наступит их время (например, после завершения асинхронной операции).
  • Event Loop: Наблюдает за стеком вызовов и очередью колбэков. Как только стек становится пустым, Event Loop берёт первую функцию из очереди и помещает её в стек.

Схема (ASCII-art)


+---------------------+
|     Call Stack      |  <--- Выполняется сейчас
+---------------------+
         ^
         |
         v
+---------------------+
|   Event Loop        |  <--- Следит за стеком и очередью
+---------------------+
         ^
         |
         v
+---------------------+
|  Callback Queue     |  <--- Ждут своей очереди
+---------------------+

Пошаговый алгоритм

  1. Выполняется основной код (например, ваш скрипт).
  2. При встрече с асинхронной операцией (например, setTimeout, запрос к файлу, HTTP-запрос) JavaScript отдаёт её на выполнение «внешним» механизмам (таймеры, файловая система и т.д.).
  3. Как только асинхронная операция завершилась, её обработчик (callback) помещается в очередь колбэков.
  4. Event Loop проверяет: если стек вызовов пуст — берёт первый колбэк из очереди и запускает его.
  5. Всё повторяется.

4. Простой пример: setTimeout

Давайте разберёмся на практике. Вот классика жанра:

console.log('A');

setTimeout(function() {
  console.log('B');
}, 0);

console.log('C');

Что будет в консоли?

Если вы только знакомитесь с асинхронностью, может показаться, что будет A, B, C — но это не так!

Результат:


A
C
B

Почему?

  • Сначала выполняется console.log('A') — в стеке вызовов, сразу выводит A.
  • Затем встречается setTimeout — JavaScript отдаёт его специальному механизму таймеров. Колбэк (функция для вывода B) будет вызван позже, даже если задержка 0 миллисекунд!
  • Выполняется console.log('C') — сразу выводит C.
  • Как только стек вызовов пуст, Event Loop берёт колбэк из очереди и выполняет его — выводит B.

Аналогия

Это как если бы вы сказали: «Поставь чайник (setTimeout), а пока напишу письмо (console.log('C'))». Когда чайник закипит (таймер сработает), вы услышите сигнал и нальёте чай (console.log('B')).

5. Пример с долгим циклом: Event Loop не волшебник!

Важно понимать: если вы напишите долгий тяжёлый код, Event Loop не сможет «проскочить» его и заняться колбэками. Пока стек вызовов не опустеет — никакие колбэки не исполняются.

console.log('Start');

setTimeout(function() {
  console.log('Timeout!');
}, 0);

for (let i = 0; i < 1e9; i++) {
  // Очень долгий цикл
}

console.log('End');

Что будет в консоли?


Start
End
Timeout!
  • Сначала Start.
  • Затем ставится таймер — колбэк попадает в очередь.
  • Потом долгий цикл. Пока цикл не закончится, Event Loop не может взять колбэк из очереди!
  • После цикла — End.
  • Только теперь Event Loop берёт колбэк и выводит Timeout!.

Вывод: Не пишите тяжёлых синхронных операций в Node.js — иначе весь сервер «зависнет» для всех клиентов!

6. Event Loop в Node.js: чуть глубже

Node.js — это не только JavaScript, но и «обвязка» вокруг него (libuv), которая умеет работать с файлами, сетью и т.д. Когда вы, например, читаете файл или принимаете HTTP-запрос, Node.js отдаёт эти задачи операционной системе, а сам продолжает работать дальше. Как только операция завершена — колбэк попадает в очередь.

В Node.js есть несколько очередей:

  • таймеры (setTimeout, setInterval)
  • I/O callbacks (например, после чтения файла)
  • idle, prepare (служебные)
  • poll (ожидание новых событий)
  • check (setImmediate)
  • close callbacks (например, после закрытия соединения)

Но для базового понимания достаточно знать: есть стек вызовов, есть очередь колбэков, и Event Loop гоняет между ними задачи.

7. Практика: асинхронное чтение файла

Давайте попробуем прочитать файл асинхронно:

const fs = require('fs');

console.log('Before');

fs.readFile('example.txt', 'utf-8', function(err, data) {
  if (err) {
    console.log('Error:', err);
  } else {
    console.log('File content:', data);
  }
});

console.log('After');

Что увидим в консоли?


Before
After
File content: ... (или ошибка)
  • Before — сразу.
  • After — сразу после, не дожидаясь чтения файла!
  • Как только файл прочитан — срабатывает колбэк и выводит содержимое.

Магия Event Loop!

8. Типичные ошибки и подводные камни

Ошибка №1: Ожидание, что асинхронный код выполнится сразу.
Многие новички думают, что если поставить setTimeout(..., 0), то код выполнится прямо сейчас. На самом деле он попадёт в очередь и выполнится только после завершения текущего стека.

Ошибка №2: Блокировка Event Loop тяжёлыми задачами.
Если вы напишете тяжёлый цикл или синхронно прочитаете большой файл (fs.readFileSync), весь Node.js «зависнет» и не сможет обрабатывать другие запросы или события. Асинхронность спасает только если вы сами не блокируете поток!

Ошибка №3: Путаница с последовательностью вывода.
Когда несколько асинхронных операций, особенно с разными задержками, новички часто путаются, в каком порядке что произойдёт. Совет: всегда мысленно рисуйте стек вызовов и очередь колбэков.

Ошибка №4: Использование глобальных переменных в колбэках.
Если вы используете переменные, которые могут измениться до того, как сработает колбэк, можно получить неожиданные результаты. Всегда помните: колбэк будет вызван позже, когда стек вызовов освободится.

1
Задача
Модуль 4: Node.js, Next.js и Angular, 2 уровень, 0 лекция
Недоступна
Асинхронность с setTimeout
Асинхронность с setTimeout
1
Задача
Модуль 4: Node.js, Next.js и Angular, 2 уровень, 0 лекция
Недоступна
Демонстрация блокировки Event Loop
Демонстрация блокировки Event Loop
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ