決して解決された約束はメモリリークを引き起こしませんか?


91

私は持っています Promiseます。必要に応じてAJAXリクエストをキャンセルするために作成しました。しかし、そのAJAXをキャンセルする必要がないので、それを解決したことがなく、AJAXは正常に完了しました。

簡略化されたスニペット:

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ライフサイクルを管理する方法について何かアドバイスはありますか?


4
「解決されなかった」という約束は、依然として「拒否された」可能性があります。あなたが探していた言葉は「満たされていない」でした。
Steven Vachon

$ httpは興味深い例です。クライアントがサーバーに到達できない場合、「timeout」引数に渡されたpromiseに関係なく、最終的にHTTPリクエストがタイムアウトする(またはエラー応答が発生する)ためです。
ryanwebjackson

回答:


144

まあ、私はあなたがそれを割り当てられたままにすることを強制するので、それへの明示的な参照を保持しないことを想定しています。

私が考えることができる最も単純なテストは、実際には多くのプロミスを割り当て、それらを解決しないことです:

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個のプロミスを割り当てるために必要なメモリが蓄積され、JSFIddleページ全体で15メガバイト未満で「そのまま」残ります。

ここに画像の説明を入力してください

反対側から、ソースコードを見ると$q

グローバルポイントから特定のプロミスへの参​​照はなく、プロミスからコールバックへの参照しかないことがわかります。コードは非常に読みやすく、明確です。コールバックから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の興味深いパターンがいくつか表示されます。時間がかかることがわかりますが、コールバックを消去できます。

ここに画像の説明を入力してください

つまり、少なくとも最新のブラウザでは、外部参照がない限り、未解決のプロミスを心配する必要はありません。


7
これは、promiseの解決に時間がかかりすぎる(ただし、最終的に解決される)場合、GCされるリスクがあることを意味しませんか?
w.brian 2014年

5
@ w.brian(たとえば、変数など)に割り当てない限り、var b = $http.get(...)またはそれにコールバックを追加します。それはそれへの参照も持っています。何かがそれを解決する場合(あなたが言ったように-解決するには長すぎて解決することを意味します)-それへの参照が必要です。だからはい-それはGCされません
ベンジャミン・グレンバウム

3
ゴッチャ、それは私が思ったことです。したがって、問題は「解決された約束がメモリリークの原因になることはありませんか?」です。答えは、コールバックがpromiseに渡される一般的なユースケースの場合、はいです。回答のこの行は、次のように矛盾しているようです。「最後の例をさらに数分間実行すると、GCの興味深いパターンがいくつか表示される場合があります。しばらくかかることがわかりますが、コールバックを消去できます。 」頭がおかしくて一生懸命でいるなら申し訳ありませんが、私はこれを確実に理解しようとしているだけです。
w.brian 2014年

1
それは私には意味をなさないようです。100.000件のプロミスを作成した場合、console.log()はなんらかの行になります。それらがなんらかの魔法によって突然解決した場合、それらの100.000がそれらの行をログに記録することを望みます。それとも、も実際のブラウザも参照していないため、ブラウザはこれが解決されないことを知っています(影響はありません)。(うーん、私はそれが本当である可能性があることを見ることができます)
odinho-Velmont '13年1

8
これらのコメントにはいくつかの真実と誤解を招くものがあるので、はっきりさせておきます。ハンドラーがアタッチされたpromiseは、ガベージコレクションの対象となる可能性があります。次のいずれかに該当する場合 Promiseは存続します(GCに対応していません)。(1)promiseオブジェクトへの参照がある、(2)promiseの「据え置き」状態への参照がある(オブジェクト/解決する/拒否するために使用する関数)。これ以外では、promiseはGCの対象となります。(誰も約束を持たず、誰もその状態を変更できない場合、とにかくその目的は何ですか?)
cdhowie
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.