节点无错误退出且不等待承诺(事件回调) [英] Node exits without error and doesn't await promise (Event callback)

查看:23
本文介绍了节点无错误退出且不等待承诺(事件回调)的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I've got a really weird issue whereby awaiting a Promise that has passed its resolve to an event-emitter callback just exits the process without error.

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".但我得到的输出是:

I'm expecting the process to halt when I run this because clearly "next" is currently never emitted. but the output I get is:

进入
添加监听器

entry
added listener

然后 nodejs 进程优雅地终止.

and then the nodejs process terminates gracefully.

我认为这与垃圾收集器有关,但 evtask 显然仍在 main 的范围内.所以我真的不知道为什么这个过程完全没有错误地退出.

I thought it was something to do with the Garbage Collector, but ev and task are clearly still in scope on main. So I'm really at a loss as to why the process exits entirely without error.

显然我最终发出该事件,但我已将我的代码简化为上述代码以进行重现.我在 node v8.7.0 上.我的代码有问题还是这是一个节点错误?

Obviously I would eventually emit the event, but I've simplified my code to the above to reproduce. I'm on node v8.7.0. Is there something wrong with my code or is this a node bug?

推荐答案

这个问题基本上就是:node是如何决定是退出事件循环还是重新来回的?

This question is basically: how does node decide whether to exit the event loop or go around again?

基本上节点会保存一个被调度的异步请求的引用计数——setTimeouts、网络请求等等.每一个被调度,这个计数就会增加,而每一个完成,这个计数就会减少.如果您到达事件循环周期的末尾并且引用计数为零,则节点退出.

Basically node keeps a reference count of scheduled async requests — setTimeouts, network requests, etc.. Each time one is scheduled, that count increases, and each time one is finished, the count decreases. If you arrive at the end of an event loop cycle and that reference count is zero node exits.

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

Simply creating a promise or event emitter does not increase the reference count — creating these objects isn't actually an async operation. For example, this promise's state will always be pending but the process exits right away:

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

p.then(console.log)

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

In the same vein this also exits after creating the emitter and registering a listener:

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

如果您希望 Node 等待一个从未安排过的事件,那么您的想法可能是 Node 不知道未来是否有可能发生的事件,但它确实如此,因为每次发生事件时它都会进行计数预定.

If you expect Node to wait on an event that is never scheduled, then you may be working under the idea that Node doesn't know whether there are future events possible, but it does because it keeps a count every time one is scheduled.

所以考虑一下这个小改动:

So consider this small alteration:

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().这与前面的示例不同,将立即退出:

As a side note, you can remove the reference to the timer with: timeout.unref(). This, unlike the previous example, will exit immediately:

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

There's a good talk about the event loop by Bert Belder here that clears up a lot of misconceptions: https://www.youtube.com/watch?v=PNa9OMajw9w

这篇关于节点无错误退出且不等待承诺(事件回调)的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆