Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/node.js/39.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 节点无错误退出并且不';t等待承诺(事件回调)_Javascript_Node.js_Promise - Fatal编程技术网

Javascript 节点无错误退出并且不';t等待承诺(事件回调)

Javascript 节点无错误退出并且不';t等待承诺(事件回调),javascript,node.js,promise,Javascript,Node.js,Promise,我遇到了一个非常奇怪的问题,即等待一个已将其resolve传递给事件发射器回调的承诺只会毫无错误地退出进程 const {EventEmitter} = require('events'); async function main() { console.log("entry"); let ev = new EventEmitter(); let task = new Promise(resolve=>{ ev.once("next", function(){r

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

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进程优雅地终止

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

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));

很明显,我最终会发出事件,但我已将代码简化为上面的代码,以进行复制。我在
节点v8.7.0
上。我的代码有问题吗?还是这是一个节点错误?

一般来说,您的代码结合了三种类似但不同的方法:异步/等待、承诺、事件侦听器。我不确定你所说的“炸弹爆炸”是什么意思。但看看代码,结果似乎是意料之中的

您的进程退出,因为您在添加事件侦听器时调用了promise。它成功解析,因此退出。如果您尝试记录任务,它将为您提供未定义的。不要在then语句中记录“exit”,而是记录结果。任务将是未定义的,因为程序不等待解析其值,并且其“代码块已完成”

您可以将代码简化为以下内容。正如您所看到的,由于调用了resolve函数,它会立即解析

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

let ev = new EventEmitter()
var p = new Promise(( resolve ) => {
    ev.once("next", resolve("Added Event Listener"));
})

p
    .then(res => console.log(res))
    .catch(e => console.log(e))

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

基本上,node保留计划异步请求的引用计数-
设置超时
、网络请求等。。每安排一次,计数就会增加,每完成一次,计数就会减少。如果到达事件循环周期的末尾,且引用计数为零,则节点退出

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

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

p.then(console.log)
同样,在创建发射器并注册侦听器后,也会出现这种情况:

const ev = new EventEmitter()
ev.on("event", (e) => console.log("event:", e))
如果您希望节点等待一个从未计划的事件,那么您可能会认为节点不知道未来是否有可能发生的事件,但确实如此,因为它会在每次计划一个事件时保持计数

请考虑这个小改动:

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的事件循环的很好的讨论,这里澄清了很多误解:

< p>我调试了好几个小时,为什么我们的一个脚本在代码<>主< <代码>函数的一行代码之后退出(没有任何错误)。这是一行
等待连接数据库(config)
。你知道吗

我发现这两个功能之间的差异至关重要:

第一:

async function connectToDatabase(config = {}) {
    if (!config.port) return;
    return new Promise(resolve => {
       resolve();
    })
}
第二:

async function connectToDatabase(config = {}) {
    return new Promise(resolve => {
       if (!config.port) return;
       resolve();
    })
}
第二个函数有时(当config.port为空时)创建从不解析的承诺,它使事件循环为空,node.js退出时认为“这里没有更多的事情要做”

自己检查一下:

// index.js - start it as node index.js
(async function main() {
  console.log('STARTED')
  await connectToDatabase()
  console.log('CONNECTED')
  console.log('DOING SOMETHING ELSE')
})()

如果使用第二个函数,则不会打印“已连接”和“正在做其他事情”,如果使用第一个函数,则不会打印“已连接”和“正在做其他事情”

hmm我怀疑答案是,一旦所有回调都设置好,并且技术上都正确,就不会再运行任何代码。“完全爆炸”是什么意思?“进程停止”(您所期望的)和“进程完全没有错误地退出”之间的区别是什么;它优雅地退出。我很确定答案是根本没有运行任何程序,也没有打开任何系统事件/文件,因此node认为程序已完成。因此,“暂停”的意思是“挂起而不退出”?是的,只是创建一个从未解决的承诺(如调用
main()
does)并不能阻止node退出—它只是垃圾收集。需要有一个实时的全局回调,比如
setTimeout
,它仍然引用承诺并可以解决它(但从来没有);您的承诺正在解决,因为您没有将
resolve
调用放入函数中,因此会立即对其进行评估。这就是想法。他不明白为什么它能这样工作,所以我用另一种方式写了出来。除了理解之外,没有什么需要“解决”的。我担心它永远不会解决,因为事件从未被发出。这个例子是人为的,最终,它归结为马克的答案所说的。我理解这一点。我试图表明,如果没有emit或setTimeout,解析(甚至嵌套)将不会阻止程序停止。但无论如何,马克的答案更清楚。好吧,我的例子是精心设计的(没有尝试)-我实际上在做stream->async iterable转换(这就是为什么我混合了这三种转换)。我猜你说的是函数返回,当你说“已解决”时,但是在承诺的上下文中,“解决”是指承诺完成时。这就是为什么你的答案乍一看似乎是错误的。@MarkMeyer-这里的答案非常有用,谢谢,让整个问题变得非常清楚。关于这一点:
node基本上保留了调度异步请求的引用计数。设置超时、网络请求等
——您知道这包括哪些内容有严格的定义吗?它与节点的核心/本机模块基本上是异步的吗?您能让您的第一个示例(Promise示例)等待吗?@SohailSi,您可以添加一个setTimeout来解析第一个示例中的Promise。然后它将等待