Verursacht ein nie gelöstes Versprechen einen Speicherverlust?

91

Ich habe eine Promise. Ich habe es erstellt, um eine AJAX-Anfrage bei Bedarf abzubrechen. Aber da ich diesen AJAX nicht abbrechen muss, habe ich ihn nie gelöst und AJAX erfolgreich abgeschlossen.

Ein vereinfachtes Snippet:

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?

Verursachen solche nie gelösten Versprechen Speicherlecks? Haben Sie Ratschläge zur Verwaltung des PromiseLebenszyklus?

Umut Benzer
quelle
4
Ein "nie gelöstes" Versprechen kann immer noch "abgelehnt" werden. Das gesuchte Wort war "unerfüllt".
Steven Vachon
$ http ist ein interessantes Beispiel, da eine HTTP-Anforderung möglicherweise eine Zeitüberschreitung aufweist (oder auf andere Weise eine Fehlerantwort liefert), wenn der Client den Server nicht erreichen kann, unabhängig von dem Versprechen, das an das Argument "Zeitüberschreitung" übergeben wurde.
Ryanwebjackson

Antworten:

144

Nun, ich gehe davon aus, dass Sie keinen expliziten Verweis darauf behalten, da dies dazu führen würde, dass es zugewiesen bleibt.

Der einfachste Test, den ich mir vorstellen kann, besteht darin, viele Versprechen zu vergeben und sie nicht zu lösen:

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

Und dann den Haufen selbst beobachten. Wie wir in den Chrome-Profiling-Tools sehen können, sammelt dies den erforderlichen Speicher, um 100 Versprechen zuzuweisen, und bleibt dann bei weniger als 15 Megabyte für die gesamte JSFIddle-Seite "dort"

Geben Sie hier die Bildbeschreibung ein

Von der anderen Seite, wenn wir uns den Quellcode ansehen$q

Wir können sehen, dass es von einem globalen Punkt aus keinen Hinweis auf ein bestimmtes Versprechen gibt, sondern nur von einem Versprechen auf seine Rückrufe. Der Code ist sehr gut lesbar und klar. Mal sehen, was passiert, wenn Sie jedoch einen Verweis vom Rückruf auf das Versprechen haben.

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

Geben Sie hier die Bildbeschreibung ein

Also nach der anfänglichen Zuweisung - es scheint, dass es auch damit umgehen kann :)

Wir können auch einige interessante Muster der GC sehen, wenn wir sein letztes Beispiel noch einige Minuten laufen lassen. Wir können sehen, dass es eine Weile dauert - aber es ist in der Lage, die Rückrufe zu bereinigen.

Geben Sie hier die Bildbeschreibung ein

Kurz gesagt - zumindest in modernen Browsern - müssen Sie sich keine Sorgen über ungelöste Versprechen machen, solange Sie keine externen Verweise darauf haben

Benjamin Gruenbaum
quelle
7
Würde dies nicht bedeuten, dass wenn ein Versprechen zu lange dauert, um es zu lösen (aber schließlich gelöst werden würde), das Risiko besteht, dass es GC wird?
w.brian
5
@ w.brian, es sei denn, Sie weisen es irgendwo zu - zum Beispiel einer Variablen: var b = $http.get(...)oder fügen Sie einen Rückruf hinzu. Das hat auch einen Bezug dazu. Wenn etwas es löst (wie Sie sagten - zu lange zum Auflösen bedeutet immer noch Auflösen), muss es einen Verweis darauf haben. Also ja - es wird nicht GC'd sein
Benjamin Gruenbaum
3
Gotcha, das habe ich mir gedacht. Die Frage lautet also: "Verursachen nie gelöste Versprechen einen Speicherverlust?" Die Antwort für den allgemeinen Anwendungsfall, in dem ein Rückruf an das Versprechen weitergeleitet wird, lautet Ja. Diese Zeile in Ihrer Antwort scheint dem zu widersprechen: "Wir können auch einige interessante Muster von GC sehen, wenn wir sein letztes Beispiel noch einige Minuten laufen lassen. Wir können sehen, dass es eine Weile dauert - aber es kann die Rückrufe bereinigen. "" Tut mir leid, wenn ich pedantisch und wählerisch bin, ich versuche nur sicherzustellen, dass ich das verstehe.
w.brian
1
Das scheint mir keinen Sinn zu ergeben. Wenn ich 100.000 Versprechungen erstellt hätte, hätte console.log () eine Zeile geschrieben. Ich möchte, dass diese 100.000 diese Zeilen protokollieren, wenn sie sich plötzlich durch Magie auflösen. Oder sagen Sie, dass der Browser wissen wird, dass dies niemals behoben werden kann, da weder ich noch der tatsächliche Browser einen Verweis darauf haben (nichts wirkt sich darauf aus) - wie könnte das jemals wahr sein? (hmm, ich kann sehen, dass das wahr sein könnte)
Odinho - Velmont
8
In diesen Kommentaren steckt etwas Wahres und etwas, das irreführend ist. Lassen Sie mich daher klarstellen: Ein Versprechen mit angehängten Handlern kann für die Speicherbereinigung in Frage kommen. Ein Versprechen wird am Leben erhalten (nicht GC-fähig), wenn eine der folgenden Bedingungen erfüllt ist: (1) es gibt einen Verweis auf das Versprechenobjekt, (2) es gibt einen Verweis auf den "zurückgestellten" Zustand des Versprechens (das Objekt) / Funktionen, mit denen Sie es auflösen / ablehnen). Abgesehen davon ist das Versprechen für GC berechtigt. (Wenn niemand das Versprechen hat und niemand seinen Zustand ändern kann, was ist dann überhaupt noch sein Zweck?)
cdhowie