web-dev-qa-db-ja.com

すべてのリスナーが削除された後、Node.jsプロセスが終了しないのはなぜですか?

次のコードでは、dataメソッドを使用してprocess.stdinonceイベントにリスナーを割り当てます。

console.log('Press Enter to allow process to terminate')
process.stdin.once('data', callback)

function callback (data) {
    console.log('Process can terminate now')
}

理論的には、コールバックが発生すると、イベントリスナーは自動的に削除され(onceでアタッチしたため)、プロセスを終了できるようになります。驚いたことに、この場合、プロセスは決して終了しません(表示されるコードがすべてです。試してみてください!)。リスナーを手動で削除しようとしましたが、何も変わりません。

おそらく私が気付いていない何かがここで起こっていますか?

16
Shawn

dataイベントリスナーを_process.stdin_に追加すると、プロセスを開いたままにするための参照が追加されます。その参照は、すべてのイベントリスナーを削除した後もそのまま残ります。あなたができることは、次のように、コールバックで手動でunref()することです。

_console.log('Press Enter to allow process to terminate')
process.stdin.once('data', callback)

function callback (data) {
    console.log('Process can terminate now')
    process.stdin.unref()
}
_

また、このようなものの一般的なデバッグツールとして、プロセスを開いたままにするもののリストを取得するために呼び出すことができる2つの(文書化されていない)関数があります。

_process._getActiveHandles()
process._getActiveRequests()
_

バックグラウンドについては、ノードプロジェクトの このプルリクエスト を参照してください。


更新:unref()'d _process.stdin_を実行した後、イベントリスナーをアタッチすることについて質問しました。これは、リスナーが自分自身と機能をアタッチすることを示す簡単な例です。

_console.log('Press Enter to allow process to terminate')
process.stdin.once('data', callback)

function callback (data) {
    console.log('Unreferencing stdin. Exiting in 5 seconds.')
    process.stdin.unref()

    process.stdin.once('data', function(data) {
        console.log('More data')
    })

    setTimeout(function() {
        console.log('Timeout, Exiting.')
    }, 5000);
}
_

そのコードでは、setTimeoutが起動する前に(5秒)別のキーを押すと、コンソールに_More data_出力が表示されます。 setTimeoutのコールバックが発生すると、プロセスは終了します。秘訣は、setTimeoutがタイマーを作成していることです。タイマーはプロセスも参照を保持します。プロセスにはまだ何かへの参照があるため、すぐには終了しません。タイマーが起動すると、タイマーが解放した参照が解放され、プロセスが終了します。これは、参照が自動的に必要なもの(この場合はsetTimeoutによって作成されたタイマー)への参照が追加(および削除)されることも示しています。

29
Mike S

.endストリームでprocess.stdinを呼び出すだけです

私にとって、これはストリームを終了するためのより簡単な(そして 文書化された )方法です。

console.log('Press Enter to allow process to terminate');
process.stdin.once('data', callback);

function callback (data) {
  console.log('Process can terminate now');
  process.stdin.end();
}

ノードがストリームをコールバック関数のコンテキストとして設定することにも注意してください。そのため、this.endを呼び出すだけで済みます。

console.log('Press Enter to allow process to terminate');
process.stdin.once('data', callback);

function callback (data) {
  // `this` refers to process.stdin here
  console.log('Process can terminate now');
  this.end();
}

endイベントを発行することもできます。これには、ストリームの終了時に関数を呼び出すことができるなどの追加の利点があります。

console.log('Press Enter to allow process to terminate');

process.stdin.once('data', function(data) {
  console.log('Process can terminate now');
  this.emit("end");
});

process.stdin.on('end', function() {
  console.log("all done now");
});

これは出力します

Press Enter to allow process to terminate

Process can terminate now
all done now

最終的な解決策は、process.exitを使用することです。これにより、いつでもプログラムを終了できます。

for (var i=0; i<10; i++) {
  process.stdout.write( i.toString() );
  if (i > 3) process.exit();
}

出力

01234

これは、子プロセスの一部として、またはその他のコードの一部として、ストリームコールバック内で機能します。

9
Thank you