Цикл событий и таймеры
Node.js выполняет ваш JavaScript в одном потоке. Пока ждёт файл или ответ сети, он не простаивает: ожидание берёт на себя система, а когда результат готов, его обработчик ставится в очередь. Цикл событий (event loop) — механизм, который по очереди достаёт эти обработчики и выполняет их. Общую идею вы видели в учебнике; здесь — что добавляет Node.js.
Порядок выполнения
Сначала выполняется весь синхронный код, затем — микрозадачи (обработчики промисов), и только потом — таймеры и обработчики ввода-вывода:
console.log('1. синхронный код');
setTimeout(() => console.log('3. таймер'), 0);
Promise.resolve().then(() => console.log('2. промис'));
console.log('1. снова синхронный код');Строки выведутся в порядке номеров: таймер с нулевой задержкой всё равно ждёт, пока закончатся синхронный код и микрозадачи.
В старом коде встречается process.nextTick(fn) — собственная очередь Node.js, похожая на микрозадачи. Её порядок относительно промисов зависит от того, CommonJS это или ES-модуль, поэтому в новом коде вместо неё пишут стандартный queueMicrotask(fn).
setImmediate
setImmediate() выполняет функцию на следующем обороте цикла, сразу после обработки ввода-вывода. Внутри обработчика чтения файла он гарантированно срабатывает раньше setTimeout(..., 0):
import { readFile } from 'node:fs';
readFile(import.meta.filename, () => {
setTimeout(() => console.log('таймер'), 0);
setImmediate(() => console.log('setImmediate'));
});Выведется «setImmediate», затем «таймер». В основном коде модуля их порядок не гарантирован, поэтому полагаться на него не стоит.
Таймеры на промисах
Модуль node:timers/promises даёт версии таймеров, которые возвращают промис. С ними паузу можно ждать через await:
import { setTimeout as sleep } from 'node:timers/promises';
console.log('Публикуем статью...');
await sleep(500);
console.log('Готово');Между строками пройдёт полсекунды. Имя sleep задано при импорте, чтобы не путать с обычным setTimeout.
Повтор по расписанию
setInterval() вызывает функцию снова и снова, clearInterval() его останавливает:
let count = 0;
const timer = setInterval(() => {
count++;
console.log(`Проверка новых комментариев №${count}`);
if (count === 3) clearInterval(timer);
}, 200);Выведутся три строки с интервалом 0,2 секунды, после чего программа завершится.
Частая ошибка
Долгое синхронное вычисление блокирует цикл событий: пока оно идёт, Node.js не обработает ни один таймер и ни один запрос. На сервере это значит, что все посетители ждут:
const start = Date.now();
setTimeout(() => console.log(`Таймер опоздал на ${Date.now() - start - 10} мс`), 10);
// Имитация тяжёлой работы: 300 мс без пауз
while (Date.now() - start < 300) {}Таймер на 10 мс сработает только после цикла — с опозданием почти на 300 мс. Тяжёлые вычисления выносят в отдельный поток — об этом урок про worker threads.