Javascript 等待但从未解决/拒绝的承诺内存使用
Javascript 等待但从未解决/拒绝的承诺内存使用,javascript,async-await,es6-promise,Javascript,Async Await,Es6 Promise,等待一个既不解决也不拒绝(从不解决/未实现)的承诺会导致内存泄漏吗 当我看到React hooks创建了新的承诺,但只解决了最后一个承诺,因此留下了许多/最不稳定的/未实现的承诺时,我对此感到好奇。序言(你可能知道这一点!): await是使用承诺回调的语法糖。(非常非常非常好的sugar。)async函数是JavaScript引擎为您构建承诺链等的函数 答复: 与此相关的不是承诺是否得到解决,而是承诺回调(以及它们引用/结束的内容)是否保留在内存中。虽然promise在内存中且未结算,但它有一
等待一个既不解决也不拒绝(从不解决/未实现)的承诺会导致内存泄漏吗
当我看到React hooks创建了新的承诺,但只解决了最后一个承诺,因此留下了许多/最不稳定的/未实现的承诺时,我对此感到好奇。序言(你可能知道这一点!):
await
是使用承诺回调的语法糖。(非常非常非常好的sugar。)async
函数是JavaScript引擎为您构建承诺链等的函数
答复:
与此相关的不是承诺是否得到解决,而是承诺回调(以及它们引用/结束的内容)是否保留在内存中。虽然promise在内存中且未结算,但它有一个对其回调函数的引用,将它们保存在内存中。有两件事使这些引用消失了:
兑现诺言,或
释放对承诺的所有引用,这使其符合GC的条件(可能,下面更多)
在正常情况下,承诺的使用者将处理程序连接到承诺,然后要么根本不保留对它的引用,要么只在处理程序关闭的上下文中保留对它的引用,而不是在其他地方。(而不是,例如,在长期存在的对象属性中保留承诺引用。)
假设debounce实现释放了它对承诺的引用,而承诺的使用者没有在这个相互引用周期之外的某个地方存储引用,那么承诺和注册到它的处理程序(以及他们持有的唯一引用的任何东西)一旦对承诺的引用被释放,所有这些都可以被垃圾收集
这需要在执行方面相当谨慎。例如(感谢您对此进行标记),如果promise对其他一些API(例如,addEventListener
)使用回调,并且回调关闭了对promise的引用,因为其他API有对回调的引用,这可能会阻止对promise的所有引用被释放,因此,在内存中保留承诺所指的任何内容(例如它的回调)
因此,这将取决于实现的谨慎程度,还有一点取决于消费者。可以编写代码来保留对承诺的引用,从而导致内存泄漏,但在正常情况下,我不希望使用者这样做。我使用以下结构进行了一些测试:
function doesntSettle() {
return new Promise(function(resolve, reject) {
// Never settle the promise
});
}
let awaited = 0;
let resolved = 0;
async function test() {
awaited++;
await doesntSettle();
resolved++;
}
setInterval(() => {
for (let i = 0; i < 100; ++i) {
test();
}
}, 1);
函数doesntSettle(){
返回新承诺(功能(解决、拒绝){
//永远不要兑现诺言
});
}
让等待=0;
设解析=0;
异步函数测试(){
等待++;
等待doesntSettle();
解析++;
}
设置间隔(()=>{
对于(设i=0;i<100;++i){
test();
}
}, 1);
在这里实施:
在Google Chrome上运行时,显示dev tools memory选项卡(但不是Performance/JS heap选项卡)中的内存使用量不断增加,这表明存在漏洞。运行这个,但解决承诺没有泄漏
运行此命令会使我的内存使用量增加1-4MB/秒。停止它并运行GC并没有释放任何内容
有趣的问题。我认为理论上不应该这样做,因为它应该不断检查承诺是否解决了,如果没有解决,等待并再次检查。不过,结果可能会因目标不同而有所不同,因为这取决于承诺是如何本机处理和/或最终多重填充的。取消承诺不应导致承诺永远无法得到解决/拒绝。而是你忽略了他们。所以它不应该影响记忆。例如,如果您创建了一个承诺,并且从未费心等待或附加到它的表,垃圾收集器将只清理东西。@Keith根据描述和使用示例,它似乎在等待创建的每个新承诺。“每个调用将返回一个承诺”,const result=wait searchAPIDebounced(文本)代码>,“只有最后一次调用返回的承诺才能解决”(它们也不会被拒绝)。不,它们都将解决或拒绝。德布斯要做的就是回报最后的承诺。描述是错误的。一个写得不好的承诺可能无法解决/拒绝,但那将是另一个问题。他正在使用可怕的命令式承诺
,所有这一切只是创建一个包装承诺,真正的物理承诺仍然会拒绝/解决。不等待某个承诺代码>遵守承诺?即使我们在其他地方没有任何对承诺的引用。@Qtax-No.await somePromise
基本上是(这里有很多手工操作)somePromise。然后(()=>{/*…等待后的代码通过函数结尾*/).catch(拒绝)
其中reject
是async
函数返回的隐式承诺的reject
。如果somePromise
确实存在一个变量(不是返回承诺的函数的替身)接下来的问题是,somePromise
中的引用持续多长时间,但如果持续多长时间(例如)一个局部变量,回调将结束,这不会阻止GC。@briosheje这是任何情况下的情况,而不仅仅是承诺。释放对任何事物的引用使其符合条件GC@Keith-这是一个很好的观点。这是实现的一个子案例,它没有发布对它永远不会解决的承诺的引用,但有点隐藏的引用我应该在上面明确指出。@briosheje当然,我们每天都在学习:)。垃圾收集的要点可以归结为区分“已使用”和“未使用”的引用,并清理“未使用”的引用,不管引用指向什么。GC将引用的概念视为