SetImmediate vs. nextTick

Node.js версия 0.10 была выпущена сегодня и представлена ​​ setImmediate. В API изменения документация предлагает использовать его при выполнении рекурсивных вызовов nextTick.

Из чего MDN говорит, он кажется очень похожим на process.nextTick.

Когда мне следует использовать nextTick и когда следует использовать setImmediate?

Ответ 1

Используйте setImmediate, если вы хотите поставить в очередь функцию за любыми обратными вызовами событий ввода-вывода, которые уже находятся в очереди событий. Используйте process.nextTick для эффективной очереди функции во главе очереди событий, чтобы она выполнялась сразу после завершения текущей функции.

Итак, в случае, когда вы пытаетесь расстаться с длинной работой, связанной с процессором, с использованием рекурсии, теперь вы хотите использовать setImmediate вместо process.nextTick для очередности следующей итерации, поскольку в противном случае любые операции ввода/Вывода не будет иметь шансов работать между итерациями.

Ответ 2

В качестве иллюстрации

import fs from 'fs';
import http from 'http';

const options = {
  host: 'www.stackoverflow.com',
  port: 80,
  path: '/index.html'
};

describe('deferredExecution', () => {
  it('deferredExecution', (done) => {
    console.log('Start');
    setTimeout(() => console.log('TO1'), 0);
    setImmediate(() => console.log('IM1'));
    process.nextTick(() => console.log('NT1'));
    setImmediate(() => console.log('IM2'));
    process.nextTick(() => console.log('NT2'));
    http.get(options, () => console.log('IO1'));
    fs.readdir(process.cwd(), () => console.log('IO2'));
    setImmediate(() => console.log('IM3'));
    process.nextTick(() => console.log('NT3'));
    setImmediate(() => console.log('IM4'));
    fs.readdir(process.cwd(), () => console.log('IO3'));
    console.log('Done');
    setTimeout(done, 1500);
  });
});

даст следующий выход

Start
Done
NT1
NT2
NT3
TO1
IO2
IO3
IM1
IM2
IM3
IM4
IO1

Надеюсь, это поможет понять разницу.

Ответ 3

В комментариях в ответе он явно не указывает, что nextTick смещается с Macrosemantics на Microsemantics.

до node 0.9 (когда был введен setImmediate), nextTick работал в начале следующего вызова.

так как node 0.9, nextTick работает в конце существующего вызова, тогда как setImmediate находится в начале следующего вызова

ознакомьтесь с https://github.com/YuzuJS/setImmediate для инструментов и деталей

Ответ 4

Я думаю, что я могу это проиллюстрировать довольно хорошо. Поскольку nextTick вызывается в конце текущей операции, вызов его рекурсивно может закончиться блокировкой цикла события от продолжения. setImmediate решает это, активируя фазу проверки цикла событий, позволяя продолжить цикл событий.

   ┌───────────────────────┐
┌─>│        timers         │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     I/O callbacks     │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
│  │     idle, prepare     │
│  └──────────┬────────────┘      ┌───────────────┐
│  ┌──────────┴────────────┐      │   incoming:   │
│  │         poll          │<─────┤  connections, │
│  └──────────┬────────────┘      │   data, etc.  │
│  ┌──────────┴────────────┐      └───────────────┘
│  │        check          │
│  └──────────┬────────────┘
│  ┌──────────┴────────────┐
└──┤    close callbacks    │
   └───────────────────────┘

источник: https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/

Обратите внимание, что фаза проверки сразу же после фазы опроса. Это связано с тем, что наиболее часто встречаются фаны опроса и обратные вызовы ввода-вывода, в результате чего ваши вызовы на setImmediate будут запущены. Таким образом, в идеале большинство этих вызовов на самом деле будут довольно быстрыми, а не так быстро, как nextTick, которые проверяются после каждой операции и технически существуют вне цикла событий.

Посмотрим на небольшой пример разницы между setImmediate и process.nextTick:

function step(iteration) {
  if (iteration === 10) return;
  setImmediate(() => {
    console.log(`setImmediate iteration: ${iteration}`);
    step(iteration + 1); // Recursive call from setImmediate handler.
  });
  process.nextTick(() => {
    console.log(`nextTick iteration: ${iteration}`);
  });
}
step(0);

Скажем, мы просто запустили эту программу и прошли первую итерацию цикла событий. Он будет вызывать функцию step с нулевой итерацией. Затем он зарегистрирует два обработчика, один для setImmediate и один для process.nextTick. Затем мы рекурсивно вызываем эту функцию из обработчика setImmediate, который будет работать в следующей фазе проверки. Обработчик nextTick будет работать в конце текущей операции, прерывая цикл события, поэтому, хотя он был зарегистрирован второй, он будет фактически запускаться первым.

Порядок заканчивается: nextTick срабатывает, когда текущая операция заканчивается, начинается цикл следующего события, выполняется обычная последовательность циклов событий, setImmediate срабатывает и рекурсивно вызывает нашу функцию step, чтобы начать процесс снова и снова. Текущая операция завершается, nextTick пожары и т.д.

Выходной код вышеуказанного кода:

nextTick iteration: 0
setImmediate iteration: 0
nextTick iteration: 1
setImmediate iteration: 1
nextTick iteration: 2
setImmediate iteration: 2
nextTick iteration: 3
setImmediate iteration: 3
nextTick iteration: 4
setImmediate iteration: 4
nextTick iteration: 5
setImmediate iteration: 5
nextTick iteration: 6
setImmediate iteration: 6
nextTick iteration: 7
setImmediate iteration: 7
nextTick iteration: 8
setImmediate iteration: 8
nextTick iteration: 9
setImmediate iteration: 9

Теперь переместите наш рекурсивный вызов на step в наш обработчик nextTick вместо setImmediate.

function step(iteration) {
  if (iteration === 10) return;
  setImmediate(() => {
    console.log(`setImmediate iteration: ${iteration}`);
  });
  process.nextTick(() => {
    console.log(`nextTick iteration: ${iteration}`);
    step(iteration + 1); // Recursive call from nextTick handler.
  });
}
step(0);

Теперь, когда мы переместили рекурсивный вызов step в обработчик nextTick, все будет вести себя в другом порядке. Наша первая итерация цикла событий выполняется и вызывает step регистрацию обработчика setImmedaite, а также обработчик nextTick. После завершения текущей операции наш обработчик nextTick запускает рекурсивные вызовы step и регистрирует другой обработчик setImmediate, а также другой обработчик nextTick. Поскольку обработчик nextTick запускается после текущей операции, регистрация обработчика nextTick в обработчике nextTick приведет к тому, что второй обработчик будет запущен сразу же после завершения текущей операции обработчика. Обработчики nextTick будут продолжать стрельбу, предотвращая продолжение цикла текущего события. Мы пройдем через все наши обработчики nextTick, пока не увидим один обработчик обработчика setImmediate.

Выход вышеприведенного кода заканчивается:

nextTick iteration: 0
nextTick iteration: 1
nextTick iteration: 2
nextTick iteration: 3
nextTick iteration: 4
nextTick iteration: 5
nextTick iteration: 6
nextTick iteration: 7
nextTick iteration: 8
nextTick iteration: 9
setImmediate iteration: 0
setImmediate iteration: 1
setImmediate iteration: 2
setImmediate iteration: 3
setImmediate iteration: 4
setImmediate iteration: 5
setImmediate iteration: 6
setImmediate iteration: 7
setImmediate iteration: 8
setImmediate iteration: 9

Обратите внимание, что если бы мы не прерывали рекурсивный вызов и прервали его после 10 итераций, то вызовы nextTick продолжали бы рекурсивно и никогда не позволяли циклу событий переходить к следующей фазе. Таким образом, nextTick может блокироваться при использовании рекурсивно, тогда как setImmediate будет срабатывать в следующем цикле событий, а другой обработчик setImmediate изнутри не будет прерывать текущий цикл событий вообще, позволяя ему продолжать выполнение фаз цикла событий как обычно.

Надеюсь, что это поможет!

PS - Я согласен с другими комментаторами в том, что имена двух функций могут быть легко заменены, поскольку nextTick звучит так, как будто он загорается в следующем цикле событий, а не в конце текущего, а конец текущий цикл более "немедленный", чем начало следующего цикла. Хорошо, что то, что мы получаем по мере созревания API, и люди зависят от существующих интерфейсов.

Ответ 5

В простых терминах process.NextTick() будет выполняться при следующем тике цикла события. Тем не менее, setImmediate, в основном имеет отдельную фазу, которая гарантирует, что обратный вызов, зарегистрированный в setImmediate(), будет вызван только после фазы обратного вызова и опроса IO.

Пожалуйста, обратитесь к этой ссылке для приятного объяснения: https://medium.com/the-node-js-collection/what-you-should-know-to-really-understand-the-node-js-event-loop-and-its-metrics-c4907b19da4c

упрощенные события цикла событий

Ответ 6

Я рекомендую вам проверить DOCS раздел, посвященный для Loop, чтобы получить лучшее понимание. Оттуда взяли фрагмент:

У нас есть два вызова, которые похожи на пользователей, но их имена сбивают с толку.

  • process.nextTick() срабатывает немедленно на той же фазе

  • setImmediate() срабатывает при следующей итерации или тике
    цикл событий

По сути, имена должны быть заменены. process.nextTick() срабатывает больше, чем setImmediate(), но это артефакт прошлого, который вряд ли изменится.