我有一个非常奇怪的问题,等待一个已经通过它的决心到事件发射器回调的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”当前从未发出过.但我得到的输出是:
entry
added listener
然后nodejs进程正常终止.
我认为这与垃圾收集器有关,但ev和任务显然仍然在主要范围内.所以我真的不知道为什么这个过程完全没有错误地退出.
显然我最终会发出这个事件,但我已将我的代码简化为上面的重现.我在节点v8.7.0上.我的代码有问题还是这个节点错误?
解决方法:
这个问题基本上是:节点如何决定是退出事件循环还是再次出现?
基本上,节点保持计划的异步请求的引用计数 – setTimeouts,网络请求等.每次计划一次,该计数增加,并且每次完成一次,计数减少.如果到达事件循环周期结束并且引用计数为零节点退出.
简单地创建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
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 [email protected] 举报,一经查实,本站将立刻删除。