为什么我的Node.js进程在删除所有侦听器后都不会终止?

Sha*_*awn 16 javascript event-listener 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')
}
Run Code Online (Sandbox Code Playgroud)

理论上,当回调被触发时,应该自动删除事件监听器(因为我附加了它once),允许进程终止.令人惊讶的是,在这种情况下,进程永远不会终止(你看到的代码就是整个东西,试试吧!).我也尝试手动删除监听器,但这没有任何改变.

还有其他事情发生在这里,我也许没有意识到吗?

Mik*_*e S 29

添加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()
}
Run Code Online (Sandbox Code Playgroud)

此外,作为这样的东西的一般调试工具,有两个(未记录的)函数可以调用以获取保持进程打开的事项列表:

process._getActiveHandles()
process._getActiveRequests()
Run Code Online (Sandbox Code Playgroud)

请参阅节点项目中的此拉取请求以获取背景信息


更新:你问到你后附加事件侦听器unref()倒是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);
}
Run Code Online (Sandbox Code Playgroud)

使用该代码,如果您在setTimeout激发之前按下另一个键(5秒),那么您将看到More data输出到控制台.一旦setTimeout回调触发,该过程将退出.诀窍在于setTimeout创建一个定时器,该过程也保留了引用.由于该过程仍然引用某些内容,因此不会立即退出.一旦计时器触发,它就会释放引用并退出进程.这也表明引用被添加(和删除)到自动需要它们的东西(setTimeout在这种情况下创建的计时器).


Tha*_*you 9

只要打电话.endprocess.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();
}
Run Code Online (Sandbox Code Playgroud)

值得注意的是,节点将流设置为回调函数的上下文,因此您只需调用即可 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();
}
Run Code Online (Sandbox Code Playgroud)

您还可以发出一个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");
});
Run Code Online (Sandbox Code Playgroud)

这会输出

Press Enter to allow process to terminate

Process can terminate now
all done now
Run Code Online (Sandbox Code Playgroud)

最终的解决方案是使用process.exit.这允许您随时终止程序.

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

产量

01234
Run Code Online (Sandbox Code Playgroud)

这将在流回调内部工作,作为子进程的一部分或任何其他代码.