Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Цикл событий и таймеры

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.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.