当您不再持有对 ES6-Promise 的引用时会发生什么情况

What happens to an ES6-Promise when you no longer hold a reference to it?

本文关键字:引用 什么情况 ES6-Promise 不再      更新时间:2023-09-26

Background

从下面的代码中可以看到:

var foo1 = new Promise (function (resolve, reject){};
var foo2 = new Promise (function (resolve, reject) {
    resolve('succes!');
});
var foo3 = new Promise (function (resolve, reject) {
    reject(Error('Failure!'));
});
console.log (typeof foo1 === 'object'); // true
console.log (Object.getOwnPropertyNames(foo1)); // []
console.log (foo1.length); // undefined
console.log (foo1); // Promise { <pending> }
console.log (foo2); // Promise { 'succes!' }
console.log (foo3); // Promise { <rejected> [Error: Failure!] }

引用Promise的变量引用了一个特殊的Promise对象,该对象包含传递给Promise构造函数的状态或结果如果随后设置:

foo1 = null;
foo2 = null;
foo3 = null;

您无法再访问此状态或结果。

问题

在上述情况下,Promise是否会被垃圾收集,如果没有,这不会造成内存泄漏的风险吗?

在上述情况下,Promise会被收集垃圾吗?

是的。在这方面,promise 对象就像所有其他对象一样。

一些实现(Firefox)确实有特殊行为,其中未处理的拒绝检测依赖于垃圾收集,但这并没有真正改变所收集的承诺对象的任何内容。

我问了另一个问题,因为我没有看到这个。但是这里的讨论,以及回答我的问题时出现的其他链接,并没有给出明确的答案。("是"和"否"都是作为答案给出的,并且没有足够的证据来知道谁是对的。

所以我设计了这个测试:

$ node --expose-gc
Welcome to Node.js v17.1.0.
Type ".help" for more information.
> const registry = new FinalizationRegistry(heldValue => { console.log(`finalizing with ${heldValue}`) });
> var stop = false; function loop(res) { if (stop) return res(); setTimeout((() => loop(res)), 3000); }
> var p = new Promise((res,rej) => loop(res)), q = p.then(()=>console.log('done')); registry.register(p, "pho", p); registry.register(q, "qux", q);
> p=q=null;
> gc()
undefined
> gc()
undefined
> gc()
undefined
> gc()
undefined
> gc()
undefined
> stop=true
true
done
> gc()
undefined
finalizing with qux
finalizing with pho

总结:p=q=null之后,我不再提及任何承诺。因此,如果它们在解析之前是 gc'able,人们会期望在循环停止之前看到最终消息,第二个 Promise 记录"完成"。但人们看不到这一点。

不过,这还不是决定性的,因为大概一个内部 Node 注册表在 setTimeout 滴答作响时保存了对它们的引用,因此也保留了对 res 参数的引用,并通过它保留了对第一个 Promise 的引用。也许第一个承诺是对第二个承诺的引用。

我也试过:

> var p = new Promise((res,rej) => { }), q = p.then(()=>console.log('done')); registry.register(p, "pho", p); registry.register(q, "qux", q); q = null;
> gc()
undefined
> gc()
undefined
> gc()
undefined
> gc()
undefined
> p = null
null
> gc()
undefined
finalizing with qux
finalizing with pho

这证实了,只要你持有对第一个承诺的引用,附加到它.then(...)承诺也会保持活动状态,即使你对后者没有任何明确的引用。但是,当你放弃对第一个承诺的引用时,即使尚未解决,它也会变得可收集,第二个承诺也是如此。

如果在最后一个示例中,我首先删除对p的引用,保留对q的引用,则p变得可收集。(在这种情况下,对q的引用并不能使p保持活力。