当您不再持有对 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
对象,其中包含 state 或 outcome 传递给 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
保持活动状态。)
背景
从下面的代码可以看出:
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
对象,其中包含 state 或 outcome 传递给 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
保持活动状态。)