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

Qta*_*tax 27 javascript async-await es6-promise

await荷兰国际集团一个Promise既不做出决议也不废品(从未结算/未实现)引起了内存泄漏?

当我看到带有更高/令人敬畏的反跳承诺的 React钩子时,我对此感到好奇,该钩子创建了新的承诺,但只解决了最后一个,因此使很多/大多数人不安/未实现。

T.J*_*der 14

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

await是用于使用promise回调的语法糖。(真的,真的,真的很好糖)async功能是在JavaScript引擎构建的承诺链,这样你的函数。

回答:

重要的不是承诺是否已实现,而是承诺回调(以及它们引用/关闭的内容)是否保留在内存中。当promise在内存中并且尚未解决时,它会引用其回调函数,将其保留在内存中。有两点使这些引用消失了:

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

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

假设debounce实现释放了对它永远不会实现的promise的引用,并且promise的使用者没有在此相互引用周期之外的某个地方存储引用,那么promise和处理程序将向其注册(以及任何它们保留唯一的引用)一旦释放了对Promise的引用,便可以全部收集垃圾。

实施方面需要相当的谨慎。例如(感谢Keith进行举报),如果promise使用其他API的回调(例如addEventListener),并且该回调在对promise的引用上关闭,因为另一个API对该回调的引用,则可能防止释放对承诺的所有引用,从而将承诺所引用的任何内容(例如其回调)保留在内存中。

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

  • @Qtax - 不。 `await somePromise` 基本上是*(这里有很多挥手)* `somePromise.then(() => { /*...await 之后的代码一直到函数末尾 */).catch (reject);` 其中的 `reject` 是 `async` 函数返回的隐式承诺的 `reject`。如果 `somePromise` 确实有一个变量(不是返回 Promise 的函数的替代品),那么就会出现 `somePromise` 中的引用持续多长时间的问题,但如果它(例如)是一个局部变量回调结束,这不会阻止 GC。 (2认同)
  • @briosheje 任何事情都是如此,而不仅仅是承诺。释放对任何内容的引用都会使其符合 GC 条件 (2认同)
  • @Keith - 这是一个非常好的观点。这是实现的一种子情况,没有释放对它永远不会解决的承诺的引用,但我应该在上面明确指出的一个有点隐藏的情况。 (2认同)

Qta*_*tax 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);
Run Code Online (Sandbox Code Playgroud)

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

在Google Chrome浏览器中运行结果框显示开发工具的“内存”标签(但在“性能/ JS堆”标签中却没有)中的内存使用量不断增加,表明存在泄漏。执行此操作但兑现承诺并没有泄漏。

运行此增加的内存使用量对我来说增加了1-4MB /秒。停止运行GC并没有释放任何它。

Google Chrome开发者工具的“内存”标签显示使用率不断提高

  • 刚刚在 Chrome v89 中对此进行了测试,现在它似乎像 Firefox 一样对这些未解决的承诺进行垃圾收集,因此不再出现内存泄漏。 (2认同)