Что такое event loop в Node.js?

MiddleNode.js · Backend·Обновлено 25 июня 2026
Коротко
Event loop — это механизм, позволяющий Node.js выполнять неблокирующие I/O-операции в однопоточной среде, делегируя тяжёлые задачи операционной системе и обрабатывая их результаты через очереди колбэков. Он постоянно проверяет очереди задач и выполняет их в строго определённом порядке фаз.

Что такое Event Loop

Event loop (цикл событий) — это фундаментальный механизм среды выполнения Node.js, реализованный через библиотеку libuv. Он позволяет однопоточному JavaScript обрабатывать тысячи параллельных операций ввода-вывода без блокировки основного потока.

Node.js не ждёт завершения медленных операций (чтение файла, сетевой запрос, запрос к БД). Вместо этого он регистрирует колбэк и продолжает выполнять другой код. Когда операция завершается, колбэк помещается в очередь, и event loop его вызывает.

Фазы Event Loop

Event loop работает циклически и проходит через 6 фаз по порядку:

1. timers

Выполняются колбэки setTimeout и setInterval, у которых истёк заданный порог времени.

2. pending callbacks

Выполняются I/O-колбэки, отложенные с предыдущей итерации (например, ошибки TCP).

3. idle, prepare

Внутреннее использование libuv, недоступно разработчику.

4. poll

Главная фаза: Node.js забирает новые I/O-события из очереди и выполняет их колбэки. Если очередь пуста — ждёт новых событий (блокируется до таймаута).

5. check

Выполняются колбэки setImmediate.

6. close callbacks

Выполняются колбэки закрытия (socket.on('close', ...))

Микрозадачи (Microtasks)

Помимо фаз существуют очереди микрозадач, которые выполняются между каждыми двумя фазами и имеют приоритет над макрозадачами:

  • process.nextTick — выполняется первой, перед Promise
  • Promise.resolve().then(...) — выполняется после nextTick
console.log('1: синхронный код');

setTimeout(() => console.log('5: setTimeout'), 0);

setImmediate(() => console.log('4: setImmediate'));

Promise.resolve().then(() => console.log('3: Promise microtask'));

process.nextTick(() => console.log('2: nextTick'));

// Вывод: 1 → 2 → 3 → 4 → 5

setTimeout(fn, 0) vs setImmediate

// Вне I/O контекста порядок не гарантирован
setTimeout(() => console.log('timeout'), 0);
setImmediate(() => console.log('immediate'));

// Внутри I/O колбэка setImmediate всегда идёт первым
const fs = require('fs');
fs.readFile(__filename, () => {
  setTimeout(() => console.log('timeout'), 0);   // второй
  setImmediate(() => console.log('immediate'));   // первый — всегда
});

Блокировка Event Loop

Длинные синхронные вычисления блокируют event loop и не дают обрабатывать новые запросы:

// Плохо: блокирует event loop на всё время вычисления
app.get('/bad', (req, res) => {
  let sum = 0;
  for (let i = 0; i < 1_000_000_000; i++) sum += i; // блокировка!
  res.json({ sum });
});

// Хорошо: выносим тяжёлые вычисления в Worker Thread
import { Worker } from 'worker_threads';

app.get('/good', (req, res) => {
  const worker = new Worker('./heavy-task.js');
  worker.on('message', (result) => res.json({ result }));
});

Практический вывод

Понимание event loop помогает:

  • избегать блокирующего кода на критических путях
  • правильно выбирать между setImmediate, setTimeout и process.nextTick
  • понимать порядок выполнения асинхронного кода при дебаге

Что хочет услышать интервьюер

Объяснение однопоточности Node.js и роли libuv в делегировании I/O операционной системе

Знание основных фаз event loop (timers → poll → check) и их назначения

Понимание разницы между микрозадачами (nextTick, Promise) и макрозадачами (setTimeout, setImmediate)

Осознание, что блокировка event loop синхронным кодом — критическая проблема для производительности

Практический пример или демонстрация порядка выполнения асинхронных колбэков

Пример: Порядок выполнения: nextTick → Promise → setImmediate → setTimeout

console.log('старт');

setTimeout(() => console.log('setTimeout'), 0);
setImmediate(() => console.log('setImmediate'));

Promise.resolve().then(() => console.log('Promise.then'));

process.nextTick(() => console.log('nextTick'));

console.log('конец синхронного кода');

// Порядок вывода:
// старт
// конец синхронного кода
// nextTick          ← микрозадача с наивысшим приоритетом
// Promise.then      ← микрозадача
// setImmediate      ← фаза check
// setTimeout        ← фаза timers (может меняться местами с setImmediate вне I/O)

Пример: Опасность блокировки event loop и решение через Worker Threads

import { Worker, isMainThread, parentPort } from 'worker_threads';
import express from 'express';

const app = express();

// Плохо: блокирует event loop — все запросы встают в очередь
app.get('/sync-heavy', (_req, res) => {
  let result = 0;
  for (let i = 0; i < 2_000_000_000; i++) result += i;
  res.json({ result });
});

// Хорошо: тяжёлая работа в отдельном потоке, event loop свободен
app.get('/async-heavy', (_req, res) => {
  const worker = new Worker(
    `
    const { parentPort } = require('worker_threads');
    let result = 0;
    for (let i = 0; i < 2_000_000_000; i++) result += i;
    parentPort.postMessage(result); // отправляем результат в главный поток
    `,
    { eval: true }
  );
  worker.once('message', (result) => res.json({ result }));
  worker.once('error', (err) => res.status(500).json({ error: err.message }));
});

app.listen(3000);

Типичные ошибки

Думают, что Node.js многопоточный из-за способности обрабатывать много запросов одновременно

Путают порядок: считают, что Promise и process.nextTick выполняются в порядке регистрации вместе с setTimeout

Не знают, что process.nextTick имеет приоритет выше Promise и может рекурсивно заблокировать event loop

Смешивают фазу poll с таймерами — не понимают, почему setTimeout(fn, 0) не всегда самый быстрый

Не могут объяснить, почему длинный цикл for внутри обработчика запроса блокирует все остальные запросы

Лучшие курсы по теме

изображение курса

Docker и Ansible

Антон Ларичев
AI-тренажерыAI-тренажеры
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Node.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Nest.js с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее