La promesse jamais résolue provoque-t-elle une fuite de mémoire?

91

J'ai un Promise. Je l'ai créé pour annuler une demande AJAX si nécessaire. Mais comme je n'ai pas besoin d'annuler cet AJAX, je ne l'ai jamais résolu et AJAX s'est terminé avec succès.

Un extrait de code simplifié:

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?

Les promesses jamais résolues comme celles-là ne provoquent-elles pas des fuites de mémoire? Avez-vous des conseils sur la façon de gérer le Promisecycle de vie?

Umut Benzer
la source
4
Une promesse «jamais résolue» peut encore être «rejetée». Le mot que vous recherchiez était «insatisfait».
Steven Vachon
$ http est un exemple intéressant car finalement une requête HTTP expirera (ou donnera autrement une réponse d'erreur), si le client ne peut pas atteindre le serveur, quelle que soit la promesse passée à l'argument 'timeout'.
ryanwebjackson

Réponses:

144

Eh bien, je suppose que vous ne gardez pas une référence explicite à celui-ci car cela le forcerait à rester alloué.

Le test le plus simple auquel je puisse penser consiste en fait à allouer beaucoup de promesses et à ne pas les résoudre:

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

Et puis regarder le tas lui-même. Comme nous pouvons le voir dans les outils de profilage de Chrome, cela accumule la mémoire nécessaire pour allouer 100 promesses, puis "y reste" à moins de 15 mégaoctets pour toute la page JSFIddle

entrez la description de l'image ici

De l'autre côté, si on regarde le $qcode source

Nous pouvons voir qu'il n'y a pas de référence d'un point global à une promesse particulière mais seulement d'une promesse à ses rappels. Le code est très lisible et clair. Voyons ce qui se passe si vous avez cependant une référence du rappel à la promesse.

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

entrez la description de l'image ici

Donc, après l'allocation initiale - il semble qu'il soit également capable de gérer cela :)

Nous pouvons également voir quelques modèles intéressants de GC si nous laissons son dernier exemple fonctionner pendant quelques minutes de plus. Nous pouvons voir que cela prend un certain temps - mais il est capable de nettoyer les rappels.

entrez la description de l'image ici

En bref - du moins dans les navigateurs modernes - vous n'avez pas à vous soucier des promesses non résolues tant que vous n'avez pas de références externes à celles-ci

Benjamin Gruenbaum
la source
7
Cela ne voudrait-il pas dire que si une promesse prend trop de temps à se résoudre (mais finit par se résoudre), elle risque d'être GC?
w.brian
5
@ w.brian sauf si vous l'assignez quelque part - par exemple à une variable: var b = $http.get(...)ou ajoutez-y un rappel. Cela y fait également référence. Si quelque chose le résout (comme vous l'avez dit - trop de temps à résoudre signifie toujours résoudre) - il doit y faire référence. Alors oui - ce ne sera pas GC'd
Benjamin Gruenbaum
3
Gotcha, c'est ce que je pensais. Donc, la question est "Les promesses jamais résolues ne causent-elles pas une fuite de mémoire?" La réponse, pour le cas d'utilisation courant où un rappel est passé à la promesse, est oui. Cette ligne dans votre réponse semble contredire cela: "Nous pouvons également voir des modèles intéressants de GC si nous laissons son dernier exemple fonctionner pendant quelques minutes de plus. Nous pouvons voir que cela prend un certain temps - mais il est capable de nettoyer les rappels. " Désolé si je suis pédant et pointilleux, j'essaie juste de m'assurer de bien comprendre cela.
w.brian
1
Cela ne me semble pas logique. Si j'avais créé 100 000 promesses, console.log () a édité une ligne. J'aimerais que ces 100.000 consignent ces lignes si elles se résolvent soudainement par magie. Ou dites-vous que le navigateur saura que cela ne résoudra jamais , puisque ni moi ni le navigateur actuel n'y font référence (rien ne l'affecte) - alors comment cela pourrait-il être vrai? (hmm, je peux voir que cela pourrait être vrai)
odinho - Velmont
8
Il y a du vrai dans ces commentaires et d'autres qui sont trompeurs, alors laissez-moi clarifier: une promesse avec des gestionnaires attachés pourrait être éligible pour le ramasse-miettes. Une promesse est maintenue vivante (non éligible au GC) si l' une des conditions suivantes est vraie: (1) il y a une référence à l'objet de la promesse, (2) il y a une référence à l'état "différé" de la promesse (l'objet / fonctions que vous utilisez pour le résoudre / le rejeter). En dehors de cela, la promesse est éligible pour GC. (Si personne n'a la promesse et que personne ne peut changer son état, quel est son but, de toute façon?)
cdhowie