Введение и проблематика
Event Loop — сердце Node.js. Это механизм, который позволяет однопоточному JavaScript обрабатывать тысячи параллельных соединений без блокировки.
Event Loop — это не часть JavaScript, а механизм среды выполнения (Node.js, браузер). V8 выполняет JavaScript, а Event Loop управляет асинхронностью.
Почему это важно?
Без понимания Event Loop невозможно:
- Писать производительный код
- Отлаживать проблемы с производительностью
- Понимать порядок выполнения асинхронного кода
- Избегать блокировки сервера
Базовая теория
Архитектура Node.js
graph TB
subgraph "Node.js"
JS[JavaScript код]
V8[V8 Engine]
EL[Event Loop]
LIBUV[libuv]
end
JS --> V8
V8 --> EL
EL --> LIBUV
LIBUV --> OS[Операционная система]
Основные компоненты
| Компонент | Описание |
|---|
| Call Stack | Стек вызовов — где выполняется синхронный код |
| Event Loop | Цикл, проверяющий очереди и выполняющий callback'и |
| Callback Queue | Очередь callback'ов готовых к выполнению |
| libuv | Библиотека для асинхронного I/O |
Call Stack (Стек вызовов)
Стек — структура LIFO (Last In, First Out), где выполняются функции:
function first() {
console.log('first');
second();
}
function second() {
console.log('second');
}
first();
// Call Stack:
// 1. first() добавляется
// 2. console.log('first') добавляется и выполняется
// 3. second() добавляется
// 4. console.log('second') добавляется и выполняется
// 5. Функции удаляются в обратном порядке
Как работает Event Loop
Упрощённая схема
Event Loop — это бесконечный цикл, который:
- Проверяет, пуст ли Call Stack
- Если пуст — берёт callback из очереди
- Помещает callback в Call Stack для выполнения
- Повторяет
graph LR
A[Call Stack пуст?] -->|Нет| B[Выполнить код]
A -->|Да| C[Проверить очередь]
C --> D[Есть callback?]
D -->|Да| E[Переместить в Call Stack]
D -->|Нет| A
E --> B
B --> A
Event Loop имеет несколько фаз выполнения (timers, I/O callbacks, poll и другие). Детали фаз рассматриваются на более продвинутых уровнях.
Практические примеры
Порядок выполнения
console.log('1. Синхронный код');
setTimeout(() => {
console.log('3. setTimeout callback');
}, 0);
console.log('2. Ещё синхронный код');
// Вывод:
// 1. Синхронный код
// 2. Ещё синхронный код
// 3. setTimeout callback
Даже с setTimeout(..., 0) callback выполнится после синхронного кода, потому что Event Loop сначала завершает весь синхронный код.
Визуализация выполнения
sequenceDiagram
participant CS as Call Stack
participant EL as Event Loop
participant TQ as Timer Queue
Note over CS: console.log('sync')
CS->>TQ: setTimeout(cb, 0)
Note over CS: console.log('sync 2')
Note over CS: Call Stack пуст
EL->>TQ: Проверка очереди
TQ->>CS: setTimeout callback
Блокировка Event Loop
// ❌ ПЛОХО: блокирует Event Loop
function blockEventLoop() {
const start = Date.now();
while (Date.now() - start < 5000) {
// Блокировка на 5 секунд
}
}
// Сервер не будет отвечать 5 секунд!
app.get('/slow', (req, res) => {
blockEventLoop();
res.send('Done');
});
⚠️
Тяжёлые вычисления блокируют Event Loop. Все запросы к серверу будут ждать, пока вычисления не завершатся.
Отношение к Main Thread
graph TB
subgraph "Main Thread"
V8[V8 Engine]
EL[Event Loop]
JS[JavaScript код]
end
subgraph "libuv Thread Pool"
T1[Thread 1]
T2[Thread 2]
T3[Thread 3]
T4[Thread 4]
end
EL --> T1
EL --> T2
EL --> T3
EL --> T4
T1 --> |callback| EL
- Main Thread — выполняет JavaScript и Event Loop
- Thread Pool (libuv) — для тяжёлых I/O операций (файлы, DNS)
- Сетевые операции выполняются через системные механизмы без блокировки
Плюсы и минусы
| Аспект | Плюсы | Минусы |
|---|
| Производительность | Отлично для I/O | Плохо для CPU-задач |
| Простота | Нет мьютексов/дедлоков | Требует понимания |
| Масштабируемость | Тысячи соединений | Один поток на процесс |
Вопросы интервьюера
Q: Что такое Event Loop простыми словами?
Это бесконечный цикл, который проверяет, есть ли задачи для выполнения, и выполняет их. Когда JavaScript-код завершается, Event Loop ищет готовые callback'и в очередях.
Q: Сколько потоков в Node.js?
JavaScript выполняется в одном потоке. Но libuv использует thread pool (по умолчанию 4 потока) для некоторых операций: работа с файлами, DNS.
Q: Как не заблокировать Event Loop?
Избегать синхронных операций с файлами на сервере, не делать тяжёлые вычисления в обработчиках запросов.
Источники