如果 Promise 永远运行,Node.JS 中会发生什么?

What happens in Node.JS if a Promise runs forever?

假设我们有这个错误函数:

async function buggy() {
  while(true) {
    await new Promise(resolve => setTimeout(resolve, 1000));
  }
}

如果你在 NodeJS 的某个地方调用它,它会永久影响服务器性能吗?

如果是这样,是否总是为 所有 不受信任的承诺设置一个像这样的故障安全机制会更好:

new Promise((resolve, reject) => {
  buggy().then(() => resolve);
  setTimeout(reject, 10000);
};

如果 promise 解析状态依赖于永不结束的外部资源/计时器(持续时间为无限),建议的故障安全将起作用并且服务器性能不会受到太大影响。

// this won’t stuck and the fail-safe will work
// (ignore the timer not being cleaned)
function buggy() {
   return new Promise(resolve => setTimeout(resolve, Infinity));
}

否则(如果 promise 是完全同步的,即解析取决于永不结束的无限循环)拒绝函数将永远不会被调用,因为事件循环被卡住,这将卡住服务器。

// The fail-safe wouldn’t work and the server is stuck
async function buggy() {
  while(true);
}

编辑: 正如 @Vlaz 所指出的,如果 buggy 函数与此类似:

async function buggy() {
  while(true) {
    await new Promise(resolve => setTimeout(resolve, 1000));
  }
}

故障保护会起作用,服务器不会卡住。

编辑 2: 在这两种情况下,您都不需要故障安全装置,因为如果故障安全装置可以工作,则意味着代码不会卡在事件循环中,并且对承诺调用 reject 不会中止有问题的代码

不,您的 buggy 功能没有任何问题。说承诺 运行s 是一种误导:承诺不会 运行。它是一个提供回调的对象。只要那个回调 resolve 没有被调用,它的 then 方法没有被调用,promise 对象就不会发生任何事情。

buggy 为 运行 时会发生以下情况:

  • new Promise 创建一个 promise 对象
  • setTimeout 是 运行 并立即完成。它注册了 resolve 回调。
  • await 被执行,实际上是在上面的 promise 上调用 then 添加一个监听器,并使 buggy return。如果这是第一次,那么它 return 是对调用者的未决承诺。
  • 一秒后(期间没有activity与buggy相关),setTimeoutAPI会把resolve回调放在相关作业队列。
  • 当事件循环检查这个队列时,它消耗并执行这个作业。 promise API(提供 resolve 回调)将 promise 置于已解决状态,并为其当时的侦听器(包括由 await 创建的侦听器)发出通知作为一项工作承诺作业队列。
  • 当事件循环检查此队列时,它会消耗并执行侦听器。这将恢复 buggy 的执行上下文,然后继续其循环。
  • 从顶部重复这些步骤

对引擎或内存的影响与使用 clearInterval 永远无法清除的 setInterval 调用相当。由于额外的 promise 相关作业(除了常规计时器作业外)每秒都会启动,并且保存的执行状态 buggy,这与您想要的相比,只会增加一点开销有一个无限生成器(使用yield)。