一尘不染

节点无错误退出并且不等待诺言(事件回调)

node.js

我遇到了一个非常奇怪的问题,即等待已将Promise传递resolve给事件-发射器回调的Promise会无错误地退出该过程。

const {EventEmitter} = require('events');

async function main() {
  console.log("entry");

  let ev = new EventEmitter();

  let task =  new Promise(resolve=>{
    ev.once("next", function(){resolve()}); console.log("added listener");
  });

  await task;

  console.log("exit");
}

main()
.then(()=>console.log("exit"))
.catch(console.log);

process.on("uncaughtException", (e)=>console.log(e));

我希望运行此程序时该过程将停止,因为当前显然从未发出过“ next”。但是我得到的输出是:

条目
添加了侦听器

然后nodejs进程正常终止。

我认为这是什么做的垃圾收集器,但evtask显然还是在范围main。因此,我对如何完全退出该过程而没有错误感到无所适从。

很显然,我 最终发出的事件,但我已经简化我的代码上面的重现。我在node v8.7.0。我的代码有问题吗?或者这是节点错误?


阅读 209

收藏
2020-07-07

共1个答案

一尘不染

这个问题基本上是:节点如何决定退出事件循环还是再次发生?

基本上,节点保留已调度的异步请求(例如setTimeouts网络请求等)的参考计数。每调度一次,该计数就会增加,而每完成一次,该计数就会减少。如果到达事件循环周期的末尾,并且引用计数为零,则退出节点。

简单地创建一个Promise或事件发射器并 不会 增加引用计数-
创建这些对象实际上并不是异步操作。例如,此promise的状态将始终处于待处理状态,但过程将立即退出:

const p = new Promise( resolve => {
    if(false) resolve()
})

p.then(console.log)

同样,在创建发射器并注册侦听器之后,这也会退出:

const ev = new EventEmitter()
ev.on("event", (e) => console.log("event:", e))

如果您希望Node等待从未计划的事件,那么您可能会想到Node不知道将来是否可能发生事件,但是这样做是因为每次计划一个事件时都会计数。

因此,请考虑以下小改动:

const ev = new EventEmitter()
ev.on("event", (e) => console.log("event:", e))

const timer = setTimeout(() => ev.emit("event", "fired!"), 1000)
// ref count is not zero, event loop will go again. 
// after timer fires ref count goes back to zero and node exits

作为附带说明,您可以使用以下命令删除对计时器的引用timeout.unref()。与前面的示例不同,这将立即退出:

const ev = new EventEmitter()
ev.on("event", (e) => console.log("event:", e))

const timer = setTimeout(() => ev.emit("event", "fired!"), 1000)
timer.unref()
伯特·贝尔德(Bert
Belder)在这里对事件循环进行了很好的讨论,这消除了许多误解:https
//www.youtube.com/watch?v=
PNa9OMajw9w
2020-07-07