1. ホーム
  2. node.js

[解決済み] nextTickとsetImmediateの比較、ビジュアル解説

2023-03-07 05:29:33

質問

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

ネクストティック

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が人々にどのように理解されるべきかについて同意していません。

ソースはこちらです。

どのように解決するのですか?

次の2つの例を考えてみましょう。

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

ネクストティック

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が起動されます。次に、イベントループの2回目の繰り返しでコールバックBが起動され、イベントループの3回目の繰り返しでコールバックCが起動される、といった具合です。これにより、イベントループがブロックされるのを防ぎ、その間に他のI/Oやタイマーのコールバックを呼び出すことができます(ループの1回目または2回目の繰り返しで起動される0msタイムアウトの場合と同様です)。

しかし、nextTickコールバックは、常に現在のコードが実行された直後、イベントループに戻る前に起動されます。nextTick の例では、イベント ループに戻る前にすべての nextTick コールバックを実行することになります。setTimeoutのコールバックはイベントループから呼び出されるため、すべてのnextTickコールバックが終了するまで、「TIMEOUT FIRED」というテキストは出力されません。