从未解决的承诺会导致内存泄漏吗?

Posted

技术标签:

【中文标题】从未解决的承诺会导致内存泄漏吗?【英文标题】:Does never resolved promise cause memory leak? 【发布时间】:2013-12-02 19:46:30 【问题描述】:

我有一个Promise。如果需要,我创建它是为了取消 AJAX 请求。但是由于我不需要取消那个 AJAX,所以我从来没有解决它并且 AJAX 成功完成。

一个简化的 sn-p:

var defer = $q.defer();
$http(url: 'example.com/some/api', timeout: defer.promise).success(function(data) 
    // do something
);

// Never defer.resolve() because I don't need to cancel that ajax. What happens to this promise after request?

从来没有解决过这样的承诺会导致内存泄漏吗?您对如何管理Promise 生命周期有什么建议吗?

【问题讨论】:

一个“从未解决”的承诺仍然可以被“拒绝”。您要查找的词是“未实现”。 $http 是一个有趣的例子,因为如果客户端无法访问服务器,无论传递给“超时”参数的承诺如何,最终 HTTP 请求都会超时(或以其他方式产生错误响应)。 【参考方案1】:

好吧,我假设您没有明确引用它,因为这会迫使它保持分配状态。

我能想到的最简单的测试实际上是分配很多承诺而不是解决它们:

var $q = angular.injector(["ng"]).get("$q");
setInterval(function () 
    for (var i = 0; i < 100; i++) 
        var $d = $q.defer();
        $d.promise;
    
, 10);

然后观察堆本身。正如我们在 Chrome 分析工具中看到的那样,这会累积分配 100 个 promise 所需的内存,然后整个JSFIddle page

“停留在那里”不到 15 兆字节

另一方面,如果我们看$q source code

我们可以看到,没有从全局点到任何特定 Promise 的引用,而只是从 Promise 到它的回调。代码非常可读和清晰。让我们看看如果您确实从回调中引用了 Promise 会怎样。

var $q = angular.injector(["ng"]).get("$q");
console.log($q);
setInterval(function () 
    for (var i = 0; i < 10; i++) 
        var $d = $q.defer();
        (function ($d)  // loop closure thing
            $d.promise.then(function () 
                console.log($d);
            );
        )($d);
    
, 10);

所以在初始分配之后 - 似乎它也能够处理它:)

如果我们让他的最后一个示例再运行几分钟,我们还可以看到一些有趣的 GC 模式。我们可以看到这需要一段时间 - 但它能够清除回调。

简而言之 - 至少在现代浏览器中 - 只要您没有对它们的外部引用,您就不必担心未解决的承诺

【讨论】:

这是否意味着如果一个 promise 需要很长时间才能解决(但会最终解决),它就有被 GC 的风险? @w.brian 除非您将其分配给某处 - 例如分配给变量:var b = $http.get(...) 或向其添加回调。这也有参考。如果某事解决了它(就像你说的 - 解决时间过长仍然意味着解决) - 它必须有一个参考。所以是的 - 它不会被 GC 处理 知道了,我就是这么想的。所以,问题是“从未解决的承诺会导致内存泄漏吗?”对于将回调传递给 Promise 的常见用例,答案是肯定的。您回答中的这一行似乎与以下内容相矛盾:“如果我们让他的最后一个示例再运行几分钟,我们还可以看到一些有趣的 GC 模式。我们可以看到这需要一段时间 - 但它能够清理回调。 "对不起,如果我是迂腐和挑剔,我只是想确保我理解这一点。 这对我来说似乎没有意义。如果我创建了 100.000 个承诺,那么 console.log() 会写上一行。如果他们突然通过某种魔法解决,我希望这 100.000 人记录这些行。或者您是说浏览器会知道这将永远解决,因为 I 和实际的浏览器都没有对它的任何引用(没有任何影响) - 那么怎么可能那是真的吗? (嗯,我知道这可能是真的) 这些 cmets 中有些道理,有些具有误导性,所以让我澄清一下:附有处理程序的承诺可能有资格进行垃圾收集。如果满足以下条件,则 Promise 将保持活动状态(不符合 GC 条件):(1) 存在对 promise 对象的引用,(2) 存在对“延迟”对象的引用承诺的状态(您用来解决/拒绝它的对象/函数)。除此之外,promise 有资格获得 GC。 (如果没有人有这个承诺,也没有人可以改变它的状态,那么它的目的是什么?)

以上是关于从未解决的承诺会导致内存泄漏吗?的主要内容,如果未能解决你的问题,请参考以下文章

存储过程会导致内存泄漏吗?

迭代器会导致内存泄漏吗?

这个 JavaScript 代码会导致内存泄漏吗?

预定的未来会导致内存泄漏吗?

JavaScript setInterval() 方法会导致内存泄漏吗?

循环 await Promise.all 内存泄漏