Skip to content

Понимание setImmediate()

Если вам нужно выполнить фрагмент кода асинхронно, но как можно скорее, одним из вариантов является использование функции setImmediate(), предоставляемой Node.js:

js
setImmediate(() => {
    // выполнить что-либо
})

Любая функция, переданная в качестве аргумента setImmediate(), является обратным вызовом, который выполняется на следующей итерации цикла событий.

Чем setImmediate() отличается от setTimeout(() => {}, 0) (передача таймаута 0 мс), а также от process.nextTick() и Promise.then()?

Функция, переданная в process.nextTick(), будет выполнена на текущей итерации цикла событий после завершения текущей операции. Это означает, что она всегда будет выполняться перед setTimeout и setImmediate.

Обратный вызов setTimeout() с задержкой 0 мс очень похож на setImmediate(). Порядок выполнения будет зависеть от различных факторов, но оба будут запущены на следующей итерации цикла событий.

Обратный вызов process.nextTick добавляется в очередь process.nextTick queue. Обратный вызов Promise.then() добавляется в очередь микрозадач promises microtask queue. Обратный вызов setTimeout, setImmediate добавляется в очередь макрозадач macrotask queue.

Цикл событий сначала выполняет задачи в очереди process.nextTick queue, затем выполняет очередь микрозадач promises microtask queue, а затем выполняет очередь макрозадач setTimeout или setImmediate macrotask queue.

Вот пример, показывающий порядок между setImmediate(), process.nextTick() и Promise.then():

js
const baz = () => console.log('baz');
const foo = () => console.log('foo');
const zoo = () => console.log('zoo');
const start = () => {
  console.log('start');
  setImmediate(baz);
  new Promise((resolve, reject) => {
    resolve('bar');
  }).then(resolve => {
    console.log(resolve);
    process.nextTick(zoo);
  });
  process.nextTick(foo);
};
start();
// start foo bar zoo baz

Этот код сначала вызовет start(), затем вызовет foo() в очереди process.nextTick queue. После этого он обработает очередь микрозадач promises microtask queue, которая выведет bar и одновременно добавит zoo() в очередь process.nextTick queue. Затем он вызовет zoo(), который только что был добавлен. В конце вызывается baz() из очереди макрозадач macrotask queue.

Указанный выше принцип верен в случаях CommonJS, но имейте в виду, что в ES-модулях, например, в файлах .mjs, порядок выполнения будет другим:

js
// start bar foo zoo baz

Это связано с тем, что загружаемый ES-модуль оборачивается как асинхронная операция, и, таким образом, весь скрипт фактически уже находится в очереди микрозадач promises microtask queue. Поэтому, когда промис немедленно разрешается, его обратный вызов добавляется в очередь микрозадач. Node.js попытается очистить очередь, прежде чем перейти к любой другой очереди, и поэтому вы увидите, что сначала выведется bar.