setImmediate()を理解する
コードを非同期に、しかしできるだけ早く実行したい場合、選択肢の1つはNode.jsが提供する setImmediate() 関数を使用することです。
(() => {
// run something
});
setImmediate()の引数として渡された関数は、イベントループの次のイテレーションで実行されるコールバックです。
setImmediate() は、setTimeout(() => {}, 0) (0msのタイムアウトを渡す)、process.nextTick()、Promise.then() とどのように違うのでしょうか?
process.nextTick() に渡された関数は、現在の操作が終了した後、イベントループの現在のイテレーションで実行されます。これは、常に setTimeout や setImmediate の前に実行されることを意味します。
0msの遅延を持つ setTimeout() のコールバックは、setImmediate() に非常によく似ています。実行順序はさまざまな要因に依存しますが、どちらもイベントループの次のイテレーションで実行されます。
process.nextTick のコールバックは process.nextTickキュー に追加されます。Promise.then() のコールバックは promisesマイクロタスクキュー に追加されます。setTimeout、setImmediate のコールバックは マクロタスクキュー に追加されます。
イベントループは、最初に process.nextTickキュー のタスクを実行し、次に promisesマイクロタスクキュー を実行し、その後に マクロタスクキュー を実行します。
setImmediate()、process.nextTick()、Promise.then() の間の順序を示す例を以下に示します。
const = () => .('baz');
const = () => .('foo');
const = () => .('zoo');
const = () => {
.('start');
();
new ((, ) => {
('bar');
}).( => {
.();
.();
});
.();
};
();
// start foo bar zoo baz
このコードはまず start() を呼び出し、次に process.nextTickキュー 内の foo() を呼び出します。その後、promisesマイクロタスクキュー を処理し、bar を出力すると同時に process.nextTickキュー に zoo() を追加します。次に、追加されたばかりの zoo() を呼び出します。最後に、マクロタスクキュー にある baz() が呼び出されます。
前述の原則はCommonJSの場合には当てはまりますが、ES Modules、例えば mjs ファイルでは実行順序が異なることに注意してください。
// start bar foo zoo baz
これは、ロードされるESモジュールが非同期操作としてラップされているため、スクリプト全体が実際には既に promisesマイクロタスクキュー に入っているからです。そのため、Promiseが即座に解決されると、そのコールバックは マイクロタスク キューに追加されます。Node.jsは他のキューに移る前にこのキューをクリアしようとするため、最初に bar が出力されるのがわかります。