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

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

背景

从下面的代码可以看出:

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 对象,其中包含 stateoutcome 传递给 Promise 构造函数的函数。如果您随后设置:

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

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

问题

在上述情况下 Promise 是否会被垃圾回收?如果没有,是否会产生导致内存泄漏的风险?

Does a Promise get garbage-collected in the above situation?

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

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

我问了 another question 这个问题,因为我没有看到这个。但是这里的讨论,以及在回答我的问题时出现的其他链接,并没有给出明确的答案。 (是和否都给出了答案,并且没有提供足够的证据来判断谁是对的。)

所以我设计了这个测试:

$ 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 之后,我不再引用任何一个 Promise。因此,如果它们在解析之前是 gc'able 的,那么人们会期望在循环停止之前看到终结消息,并且第二个 Promise 记录为“完成”。但是没有人看到这一点。

虽然这不是决定性的,因为可能是内部节点注册表在滴答时持有对 setTimeouts 的引用,因此也指向 res 参数,并通过它指向第一个承诺。也许第一个 Promise 持有对第二个 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

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

如果在最后一个示例中,我首先删除对 p 的引用,保留对 q 的引用,那么 p 就可以收集了。 (在这种情况下对 q 的引用不会使 p 保持活动状态。)