← Назад к вопросам

Что будет делать Node.js, когда стек вызовов и очереди цикла событий окажутся пустыми?

1.8 Middle🔥 71 комментариев
#Node.js и JavaScript

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI30 мар. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Поведение Node.js при пустых стеке вызовов и очередях

Это ключевой вопрос о жизненном цикле приложения Node.js. Когда стек вызовов и все очереди пусты, Node.js ведёт себя в зависимости от наличия активных обработчиков и таймеров.

Общий сценарий: завершение приложения

Если нет активных операций, ожидающих обработки, Node.js завершит процесс:

console.log("Начало");
// Стек вызовов опустел
// Очередь пуста
// Нет активных таймеров или операций ввода-вывода
// → Процесс завершится

// Эта строка никогда не выполнится
setTimeout(() => console.log("Never"), 1000);

Результат: программа выводит "Начало" и завершается.

Детальное объяснение: жизненный цикл события

Node.js проходит фазы цикла событий в порядке:

  1. Timers — таймеры (setTimeout, setInterval)
  2. Pending callbacks — отложенные операции
  3. Idle, prepare — внутренние операции Node.js
  4. Poll — операции ввода-вывода
  5. Check — setImmediate()
  6. Close callbacks — закрытие ресурсов

Когда Node.js завершает процесс?

Node.js завершается, когда:

// Сценарий 1: Нет активных обработчиков
console.log("Start");
// Процесс выйдет с кодом 0
process.exit(0);  // Явное завершение

На практике, Node.js проверяет ref count — количество активных дескрипторов, которые "интересны" приложению:

const fs = require("node:fs");
const net = require("node:net");

// Сценарий 1: Запущен сервер (активный дескриптор)
const server = net.createServer();
server.listen(3000);
// Процесс будет работать, ожидая соединений

// Сценарий 2: Явно деактивировать дескриптор
server.unref();  // Теперь сервер не будет удерживать процесс
// Если других активных дескрипторов нет, процесс завершится

// Сценарий 3: Вернуть дескриптор в активное состояние
server.ref();    // Процесс будет работать снова

Практические примеры

Пример 1: Таймер с пустой очередью

console.log("Start");

setTimeout(() => {
  console.log("Timeout");
}, 5000);

// После этой строки:
// - Стек вызовов: пуст
// - Очередь таймеров: setTimeout зарегистрирован
// - Node.js будет спать, ожидая таймера
// - Через 5 сек выполнит колбэк, потом завершится

Пример 2: Сервер, ожидающий соединений

const http = require("node:http");

const server = http.createServer((req, res) => {
  res.writeHead(200);
  res.end("OK");
});

server.listen(3000);
console.log("Server listening on port 3000");

// - Стек вызовов: пуст
// - Очередь событий: пуста
// - Сервер имеет активный дескриптор (слушает порт)
// - Процесс НЕ завершится, будет ждать соединений

Пример 3: Отключение удержания процесса

const server = http.createServer();
server.listen(3000);

// Отключаем удержание процесса
server.unref();

console.log("Setup complete");

// Результат:
// - Выводит "Setup complete"
// - Сервер слушает, но процесс не удерживает его
// - Если других активных ресурсов нет, процесс завершится

Три сценария поведения

СценарийЧто будетПример
Активные дескрипторыПроцесс работаетСервер, слушающий порт
Активные таймерыПроцесс ждётsetTimeout с большой задержкой
Ничего активногоПроцесс выходитСинхронный код, потом ничего

Управление жизненным циклом

const http = require("node:http");

const server = http.createServer();
server.listen(3000);

// Неактивный таймер не удерживает процесс
const timer = setTimeout(() => {}, 10000);
timer.unref();  // Деактивировать

// Активный таймер удерживает процесс
const timer2 = setInterval(() => {
  console.log("Tick");
}, 1000);
// timer2.unref() для отключения удержания

// Обработчик сигнала (активный дескриптор)
process.on("SIGTERM", () => {
  server.close(() => {
    console.log("Server closed");
    process.exit(0);
  });
});

Важное замечание: Event Loop продолжает работать

Когда стек и очереди пусты, но есть активные дескрипторы:

// Node.js не "спит" - он использует epoll/kqueue для эффективного ожидания
// Это позволяет обрабатывать события с минимальной нагрузкой на CPU