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

时间:2014-09-23 20:51:19

标签: javascript node.js event-listener

在以下代码中,我使用data方法为process.stdin once事件分配了一个监听器。

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

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

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

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

2 个答案:

答案 0 :(得分:25)

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()
}

此外,作为此类内容的一般调试工具,您可以调用两个(未记录的)函数来获取保持流程开放的事项列表:

process._getActiveHandles()
process._getActiveRequests()

请参阅节点项目中的this pull request了解背景信息。


更新:您询问了在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创建的计时器)。

答案 1 :(得分:9)

只需在.end信息流

上拨打process.stdin即可

对我而言,这是一种更直接(和documented)结束流的方式。

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

这可以在流回调中工作,作为子进程的一部分,或任何其他代码。