NextTick vs setImmediate, визуальное объяснение

Я очень смущен различиями между nextTick и setImmediate. Я прочитал всю документацию о них в Интернете, но я до сих пор не понимаю, как они работают.

Примеры:

function log(n) { console.log(n); }

setImmediate

setImmediate(function() {
  setImmediate(function() {
    log(1);
    setImmediate(function() { log(2); });
    setImmediate(function() { log(3); });
  });
  setImmediate(function() {
    log(4);
    setImmediate(function() { log(5); });
    setImmediate(function() { log(6); });
  });
});

//1 2 3 4 5 6

nextTick

process.nextTick(function() {
  process.nextTick(function() {
    log(1);
    process.nextTick(function() { log(2); });
    process.nextTick(function() { log(3); });
  });
  process.nextTick(function() {
    log(4);
    process.nextTick(function() { log(5); });
    process.nextTick(function() { log(6); });
  });
});

//1 4 2 3 5 6

Почему эти результаты? Пожалуйста, объясните с помощью визуального или очень простого объяснения. Даже разработчики node не согласны с тем, как nextTick и setImmediate должны быть поняты людьми.

Источники:

Ответ 1

Рассмотрим следующие два примера:

setImmediate

setImmediate(function A() {
  setImmediate(function B() {
    log(1);
    setImmediate(function D() { log(2); });
    setImmediate(function E() { log(3); });
  });
  setImmediate(function C() {
    log(4);
    setImmediate(function F() { log(5); });
    setImmediate(function G() { log(6); });
  });
});

setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0)

// 'TIMEOUT FIRED' 1 4 2 3 5 6
// OR
// 1 'TIMEOUT FIRED' 4 2 3 5 6

nextTick

process.nextTick(function A() {
  process.nextTick(function B() {
    log(1);
    process.nextTick(function D() { log(2); });
    process.nextTick(function E() { log(3); });
  });
  process.nextTick(function C() {
    log(4);
    process.nextTick(function F() { log(5); });
    process.nextTick(function G() { log(6); });
  });
});

setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0)

// 1 4 2 3 5 6 'TIMEOUT FIRED'

setImmediate обратные вызовы выходят из цикла событий один раз на итерацию в том порядке, в котором они были поставлены в очередь. Итак, на первой итерации цикла события выполняется обратный вызов A. Затем на второй итерации цикла события обратный вызов B запускается, затем на третьей итерации обратного вызова цикла цикла C и т.д. Это предотвращает блокировку цикла событий и позволяет другим обратным вызовам ввода-вывода или таймера (как в случае тайм-аута 0мс, который запускается на итерации 1-го или 2-го цикла).

nextTick обратные вызовы, однако, всегда запускаются сразу же после выполнения текущего кода и перед возвратом в цикл событий. В примере nextTick мы завершаем все последующие обратные вызовы nextTick, прежде чем возвращаться к циклу событий. Поскольку callback callTimeout вызывается из цикла событий, текст "TIMEOUT FIRED" не будет выводиться до тех пор, пока мы не закончим с каждым последующим обратным вызовом.

Ответ 2

Согласно документу Node.js имена этих двух функций точно поменялись местами.

setImmediate() (ЛУЧШИЙ РЕКОМЕНДУЕТСЯ)

Он запускается первым в очереди событий


process.nextTick() (ИСПОЛЬЗУЙТЕ ДЛЯ ОСОБЫХ СЛУЧАЙ, см. пример позже)

Он сразу же запускается, он в конце пишет текущий файл


Если у нас есть этот код

setTimeout(function(){
  console.log('Hello world 5'); // It waiting like a normal person at a queue
}, 0);

setImmediate(function(){
  console.log('Hello world 4'); 
  // It like get to last and be take care of first 
  // but always after of .nextTick and before of setInterval(, 0)
});

process.nextTick(function(){
   console.log('Hello world 3'); // It like be at the bottom at this file
});

console.log('Hello world 1');
console.log('Hello world 2');

Визуальное объяснение согласно вашему запросу:

enter image description here

Случаи использования process.nextTick(), когда вы должны произвести событие и событие, прежде чем его обработать:

const EventEmitter = require('events');
const util = require('util');

function MyEmitter() {
  EventEmitter.call(this);

  // use nextTick to emit the event once a handler is assigned
  process.nextTick(function () {
    this.emit('event');
  }.bind(this));
}
util.inherits(MyEmitter, EventEmitter);

const myEmitter = new MyEmitter();
myEmitter.on('event', function() {
  console.log('an event occurred!');
});

Посмотрите на этот видеоролик, где Филип Робертс дает нам отличное объяснение о цикле событий во время выполнения, и посмотрите на этот онлайн-отладчик Eventloop Live Test, как работает цикл событий.

Источник: https://github.com/nodejs/node/blob/master/doc/topics/the-event-loop-timers-and-nexttick.md#processnexttick-vs-setimmediate

Ответ 3

Я не могу воспроизвести ваши результаты для setImmediate. Он должен быть таким же, как nextTick (и он находится в моих тестах), поскольку в этой ситуации они делают почти то же самое. Единственным разумным объяснением этого является то, что setImmediate как-то синхронно, но это не так.

И согласно документации NodeJS единственное реальное различие заключается в том, что несколько nextTick могут срабатывать в одной итерации цикла (в зависимости от maxTickDepth), а setImmediate срабатывает один раз на итерацию.

Ответ 4

Ниже приведена лучшая ясность.

setImmediate

  • Выполняет script после завершения текущей фазы опроса.
  • Функция таймера и функции таймера являются глобальными, вы можете вызывать их без require.
  • Он может быть очищен clearImmediate().
  • Установите "немедленное" выполнение обратного вызова после обратных вызовов событий ввода-вывода перед setTimeout() и setInterval().

nextTick

  • Это глобальная функция процесса процесса NodeJS.
  • Все обратные вызовы, переданные process.nextTick(), будут разрешены до того, как цикл события будет продолжен.
  • Разрешить пользователям обрабатывать ошибки.
  • Помогает снова запросить запрос до того, как цикл цикла будет продолжен.

Простой фрагмент кода.

console.log("I'm First");

setImmediate(function () {
  console.log('Im setImmediate');
});

console.log("I'm Second");

process.nextTick(function () {
  console.log('Im nextTick');
});

console.log("I'm Last");

/*
Output
$ node server.js
I'm First
I'm Second
I'm Last
Im nextTick
Im setImmediate
*/

Ответ 5

Я думаю, что все приведенные выше ответы устарели, потому что я постоянно получал разные ответы с текущей версией nodejs, и легко рассуждать о

var log=console.log
log(process.version)

var makeAsyncCall
if(false)
    makeAsyncCall=setImmediate
else
    makeAsyncCall=process.nextTick;

makeAsyncCall(function A () {
    makeAsyncCall(function B() {
        log(1);
        makeAsyncCall(function C() { log(2); });
        makeAsyncCall(function D() { log(3); });
    });
    makeAsyncCall(function E() {
        log(4);
        makeAsyncCall(function F() { log(5); });
        makeAsyncCall(function G() { log(6); });
    });
});
//1
//4
//2
//3
//5
//6
//in both case

После чтения https://github.com/nodejs/node/blob/master/doc/topics/the-event-loop-timers-and-nexttick.md#processnexttick-vs-setimmediate давайте начнем с setImmediate, мы должны отслеживать check queue, потому что там находится обратный вызов setImmediate.

Первая итерация

A нажимается на check queue

проверить очередь: [A]

Вторая итерация

A вытаскивается из queue для выполнения

Во время его выполнения он помещает B и E в queue, а затем A завершает и запускает следующую итерацию

проверить очередь: [B, E]

Третья итерация

вытащите B и нажмите C D

проверить очередь: [E, C, D]

Четвертая итерация

вытащите E и нажмите F G

проверить очередь: [C, D, F, G]

Наконец

выполнить обратные вызовы в очереди последовательно

В случае nextTick очередь работает точно так же, поэтому она дает тот же результат

Другое:

nextTickQueue будет обработан после текущей операции завершает, независимо от текущей фазы цикла события

Чтобы быть ясным, цикл событий поддерживает несколько очередей, а check queue - только один из них, node будет решать, какую очередь использовать на основе некоторых правил

с process.nextTick однако, он вроде обходит все правило и выполняет обратный вызов в nextTick немедленно