如果 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
相关),setTimeout
API会把resolve
回调放在相关作业队列。
- 当事件循环检查这个队列时,它消耗并执行这个作业。 promise API(提供
resolve
回调)将 promise 置于已解决状态,并为其当时的侦听器(包括由 await
创建的侦听器)发出通知作为一项工作承诺作业队列。
- 当事件循环检查此队列时,它会消耗并执行侦听器。这将恢复
buggy
的执行上下文,然后继续其循环。
- 从顶部重复这些步骤
对引擎或内存的影响与使用 clearInterval
永远无法清除的 setInterval
调用相当。由于额外的 promise 相关作业(除了常规计时器作业外)每秒都会启动,并且保存的执行状态 buggy
,这与您想要的相比,只会增加一点开销有一个无限生成器(使用yield
)。
假设我们有这个错误函数:
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
相关),setTimeout
API会把resolve
回调放在相关作业队列。 - 当事件循环检查这个队列时,它消耗并执行这个作业。 promise API(提供
resolve
回调)将 promise 置于已解决状态,并为其当时的侦听器(包括由await
创建的侦听器)发出通知作为一项工作承诺作业队列。 - 当事件循环检查此队列时,它会消耗并执行侦听器。这将恢复
buggy
的执行上下文,然后继续其循环。 - 从顶部重复这些步骤
对引擎或内存的影响与使用 clearInterval
永远无法清除的 setInterval
调用相当。由于额外的 promise 相关作业(除了常规计时器作业外)每秒都会启动,并且保存的执行状态 buggy
,这与您想要的相比,只会增加一点开销有一个无限生成器(使用yield
)。