Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/401.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript 等待但从未解决/拒绝的承诺内存使用_Javascript_Async Await_Es6 Promise - Fatal编程技术网

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将引用的概念视为