等待但从未解决/拒绝的承诺内存使用情况

时间:2019-08-08 12:18:49

标签: javascript async-await es6-promise

await Promise既不解决也不拒绝(从不解决/未实现)会导致内存泄漏吗?

我对使用slorber/awesome-debounce-promise的React钩子感到好奇,该钩子创建了新的承诺,但仅解决了最后一个,因此使很多/大多数人不安/未实现。 < / p>

2 个答案:

答案 0 :(得分:14)

序言(您可能知道这一点!):

await是用于使用promise回调的语法糖。 (真的,真的,真的很好糖。) async函数是JavaScript引擎为您构建承诺链等的函数。

答案:

相关的问题不在于是否实现了诺言,而是是否在内存中保留了诺言回调(以及它们所引用/结束的事情)。当promise在内存中并且尚未解决时,它会引用其回调函数,将其保留在内存中。有两点使这些引用消失了:

  1. 履行诺言,或者
  2. 释放对承诺的所有引用,使其有资格获得GC(可能在下文中有更多说明)

在通常情况下,promise的使用者将处理程序挂接到该promise,然后要么根本不保留对它的引用,要么仅在处理程序功能关闭且上下文中保留对它的引用。没有其他地方。 (而不是例如,将promise引用保留在长期存在的对象属性中。)

假设debounce实现释放了对它永远不会实现的promise的引用,并且promise的使用者没有在此相互引用周期之外的某个地方存储引用,那么promise和处理程序将向其注册(一旦发布了对诺言的引用,所有它们拥有的唯一引用都可以被垃圾回收。

在实现方面需要相当的谨慎。例如(感谢Keith进行举报),如果promise使用其他API的回调(例如addEventListener),并且该回调在对由于其他API都具有对回调的引用,因此可以防止释放对承诺的所有引用,从而将承诺所引用的任何内容(例如其回调)保留在内存中。

因此,这将取决于实现的谨慎性,并取决于消费者。可能会编写代码来保留对Promise的引用,从而导致内存泄漏,但是在正常情况下,我不希望消费者这样做。

答案 1 :(得分:5)

我使用以下结构进行了一些测试:

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);

在此处实现:https://codesandbox.io/s/unsetteled-awaited-promise-memory-usage-u44oc

在Google Chrome中运行just the result frame表示在开发工具的“内存”标签(但不在“性能/ JS堆”标签下)中的内存使用量不断增加,表明存在泄漏。执行此操作但兑现承诺并没有泄漏。

运行此增加的内存使用量增加了1-4MB /秒。停止运行GC并不能释放其中的任何内容。

Google Chrome dev tools Memory tab showing increasing usage