setImmediateとnextTick


336

Node.jsバージョン0.10が本日リリースされ、導入されました setImmediateAPIの変更のドキュメントには、再帰やったときに、それを使用することを提案してnextTick電話を。

何からMDNは述べています、それは非常によく似らしいですprocess.nextTick

いつ使用nextTickすべきsetImmediateか、いつ使用すべきか?



1
パフォーマンスベンチマークから、大規模な計算nextTickよりも高速に見えsetImmediateます。

10
記録のために、私は最初にこれらの5つの段落を読みましたが、それでも実際には何も明らかにされなかったときに、この質問に終わりました。受け入れられた回答ははるかに簡潔であり、実際には何をするかsetImmediateをより詳細に説明しています。
Chev

違いをブログで詳しく説明しました。
plafer

GCは前setImmediateに実行できても、前に実行できないのnextTickですか?

回答:


510

setImmediateイベントキューに既にあるI / Oイベントコールバックの背後にある関数をキューに入れる場合に使用します。process.nextTick現在の関数が完了した直後に実行されるように、イベントキューの先頭で関数を効果的にキューに入れるために使用します。

したがって、再帰を使用して長時間実行されているCPUにバインドされたジョブを分割しようとしている場合は、次の反復をキューに入れるのではsetImmediateなく、使用することをお勧めします。process.nextTick反復の間に実行します。


86
process.nextTickに渡されるコールバックは通常、現在の実行フローの最後に呼び出されるため、関数を同期的に呼び出すのとほぼ同じくらい高速です。オフのままにすると、イベントループが不足し、I / Oが発生しなくなります。setImmediatesは作成された順序でキューに入れられ、ループの反復ごとに1回キューからポップされます。これは、反復ごとにキューに入れられたprocess.maxTickDepthコールバックを実行するprocess.nextTickとは異なります。setImmediateは、I / Oが不足していないことを確認するためにキューに入れられたコールバックを起動した後、イベントループに譲ります。
Benjamin Gruenbaum 2013

2
@UstamanSangat setImmediateはIE10 +でのみサポートされています。他のすべてのブラウザーは、Microsoftに打ちのめされたくないため、将来の標準の実装を頑固に拒否しています。FF / Chromeで同様の結果を得るには、postMessage(メッセージを自分のウィンドウに投稿する)を使用できます。特に更新がUI関連である場合は、requestAnimationFrameの使用も検討できます。setTimeout(func、0)は、process.nextTickのようには機能しません。
fabspro 2013

44
@fabsproは「私のMicrosoftに打ちのめされるのが嫌いだから」と言って、何かが痛い。それはほとんどそれがひどく、ひどく名前が付けられているからです。setImmediate関数が一度も実行されない場合は、すぐに実行されます。関数の名前は、その機能とは正反対です。nextTickとsetImmediateは、スイッチを入れたほうがよいでしょう。現在のスタックが完了した直後(I / Oを待機する前)にsetImmediateが実行され、(I / Oを待機した後)次のティックの終わりにnextTickが実行されます。しかし、これはすでに1000回言われています。
クレイグアンドリュース

4
@fabsproしかし、残念ながらこの関数はnextTickと呼ばれています。nextTickは「即時」に実行されますが、setImmediateはsetTimeout / postMessageに似ています。
Robert

1
@CraigAndrews requestAnimationFrame常に発生するわけではないので回避します(これは間違いなく見ました。例はタブが現在のタブではなかったと思います)。ページがペイントを完了する前に呼び出される可能性があります(つまり、ブラウザはまだビジー状態です)。
robocat 2014年

68

例として

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

これが違いを理解するのに役立つことを願っています。

更新しました:

コールバックはprocess.nextTick()、他のI​​ / Oイベントが発生する前にrunを使用して延期されますが、setImmediate()を使用すると、実行はすでにキューにあるI / Oイベントの後ろにキューイングされます。

Node.js Design Patterns、Mario Casciaro作(おそらくnode.js / jsに関する最高の本)


2
これは本当に役立つ感謝です。画像と例が何かを理解する最も速い方法だと思います。
ジョンジェームズ

1
I / Oサイクル内でない場合、setTimeout()およびsetImmediate()は、プロセスのパフォーマンスによっては順序が非決定的であることを指摘することが重要だと思います。nodejs.org/en/docs/guides/event-loop-timers-and-nexttick For example, if we run the following script which is not within an I/O cycle (i.e. the main module), the order in which the two timers are executed is non-deterministic, as it is bound by the performance of the process: したがって、この回答は実際の違いには答えませんが、異なるコンテキストで変化する可能性のある例にすぎません
Actung

@Actungが指摘したように。結果を判断するには、setTimetoutとsetImmediateがI / Oサイクル内にあるかどうかを知ることが非常に重要です。
Rajika Imal、2018

50

私はこれをかなりうまく説明できると思います。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/

チェックフェーズがポーリングフェーズの直後にあることに注意してください。これは、ポーリングフェーズとI / Oコールバックが、呼び出し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反復ゼロで関数を呼び出します。これは、2つのハンドラ、のための1つ登録されますsetImmediateとのための1つを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);

今、我々は再帰呼び出しを移動したことstepnextTickハンドラ物事異なる順序で動作します。イベントループの最初の反復が実行stepされ、setImmedaiteハンドラーとハンドラーの登録を呼び出しますnextTick。現在の操作が終了すると、nextTickハンドラーが起動し、別のハンドラーだけでなく別のハンドラーも再帰的に呼び出しstepて登録します。以来登録現在の操作の後、ハンドラ火災、内のハンドラを、ハンドラは、第二のハンドラは、現在のハンドラの動作が終了した直後に実行します。ハンドラは、これまで続けてから、現在のイベントループを防止し、焼成を維持します。私たちはすべてを乗り越えますsetImmediatenextTicknextTicknextTicknextTicknextTicknextTickハンドラーが1つ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-2つの関数の名前はnextTick現在のループの終わりではなく次のイベントループで起動するように聞こえるため、2つの関数の名前を簡単に入れ替えることができ、現在のループの終わりがより「すぐ"次のループの始まりよりも。まあ、それはAPIが成熟し、人々が既存のインターフェースに依存するようになったときに得られるものです。


2
かなり明確な説明。この回答にはもっと賛成票が必要だと思います。
Actung 2018年

よく説明されている(Y)
Dhiraj Sharma

process.nextTickの使用に関するNodeの警告を繰り返すことが重要です。nextTickQueueに多数のコールバックをエンキューする場合、ポーリングフェーズに到達しないようにすることで、イベントループを枯渇させる可能性があります。これが、一般的にsetImmediateを好む理由です。
faridcs

1
ありがとう、これは最高の説明でした。サンプルコードは本当に役に立ちました。
skyhavoc

@skyhavoc私が助けてくれてうれしい!
Chev

30

回答のコメントでは、nextTickがMacrosemanticsからMicrosemanticsに移行したことを明示的に述べていません。

ノード0.9以前(setImmediateが導入されたとき)、nextTickは次のコールスタックの開始時に動作しました。

ノード0.9以降、nextTickは既存の呼び出しスタックの最後で動作しますが、setImmediateは次の呼び出しスタックの先頭にあります

ツールと詳細については、https://github.com/YuzuJS/setImmediateを確認してください


11

簡単に言えば、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

簡略化されたイベントループイベント


8

両方がどのように機能するかを詳しく説明するいくつかの素晴らしい答えがあります。

尋ねられた特定の質問に答えるものを追加するだけです:

いつ使用nextTickすべきsetImmediateか、いつ使用すべきか?


常に使用してくださいsetImmediate


Node.jsのイベントループ、タイマ、およびprocess.nextTick()ドキュメントは、次のものが含まれます。

推論setImmediate()するのが簡単であり、ブラウザのJSなどのさまざまな環境と互換性のあるコードにつながるため、開発者はすべての場合に使用することをお勧めします。


以前のドキュメントでは、次の原因になるprocess.nextTick可能性があると警告しています...

これ再帰的なprocess.nextTick()呼び出しを行うことでI / Oを「枯渇」させることができるため、イベントループがポーリングフェーズに到達できなくなるためいくつかの悪い状況になります

process.nextTick結局のところ、餓死することさえありPromisesます:

Promise.resolve().then(() => { console.log('this happens LAST'); });

process.nextTick(() => {
  console.log('all of these...');
  process.nextTick(() => {
    console.log('...happen before...');
    process.nextTick(() => {
      console.log('...the Promise ever...');
      process.nextTick(() => {
        console.log('...has a chance to resolve');
      })
    })
  })
})

一方、setImmediateは「推論しやすい」であり、次のタイプの問題を回避します。

Promise.resolve().then(() => { console.log('this happens FIRST'); });

setImmediate(() => {
  console.log('this happens LAST');
})

したがって、の固有の動作に特別な必要がない限りprocess.nextTick、推奨されるアプローチは「すべての場合に使用setImmediate()する」ことです。


1

よりよく理解するために、Loop専用のドキュメントセクションを確認することをお勧めします。そこから抜粋したスニペット:

ユーザーに関する限り、似た2つの呼び出しがありますが、その名前は混乱を招きます。

  • process.nextTick()は同じフェーズですぐに起動します

  • setImmediate()は、
    イベントループの次の反復または「ティック」で起動します

本質的に、名前は交換する必要があります。process.nextTick()はsetImmediate()よりもすぐに起動しますが、これは変更される可能性が低い過去のアーティファクトです。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.