¿La promesa nunca resuelta causa pérdida de memoria?

91

Tengo un Promise. Lo creé para cancelar una solicitud AJAX si es necesario. Pero como no necesito cancelar ese AJAX, nunca lo resolví y AJAX se completó con éxito.

Un fragmento simplificado:

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?

¿Promesas nunca resueltas como esa causan pérdidas de memoria? ¿Tiene algún consejo sobre cómo gestionar el Promiseciclo de vida?

Umut Benzer
fuente
4
Una promesa "nunca resuelta" todavía puede ser "rechazada". La palabra que buscaba estaba "incumplida".
Steven Vachon
$ http es un ejemplo interesante porque eventualmente una solicitud HTTP expirará (o de lo contrario producirá una respuesta de error), si el cliente no puede alcanzar el servidor, independientemente de la promesa pasada al argumento 'timeout'.
ryanwebjackson

Respuestas:

144

Bueno, supongo que no mantiene una referencia explícita a él, ya que eso lo obligaría a permanecer asignado.

La prueba más simple que se me ocurre es asignar muchas promesas y no resolverlas:

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

Y luego mirando el montón en sí. Como podemos ver en las herramientas de creación de perfiles de Chrome, esto acumula la memoria necesaria para asignar 100 promesas y luego simplemente "permanece allí" a menos de 15 megabyes para toda la página JSFIddle.

ingrese la descripción de la imagen aquí

Desde el otro lado, si miramos el $qcódigo fuente

Podemos ver que no hay referencia desde un punto global a ninguna promesa en particular, sino solo de una promesa a sus devoluciones de llamada. El código es muy legible y claro. Veamos qué sucede si, sin embargo, tiene una referencia de la devolución de llamada a la promesa.

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

ingrese la descripción de la imagen aquí

Entonces, después de la asignación inicial, parece que también puede manejar eso :)

También podemos ver algunos patrones interesantes de GC si dejamos que su último ejemplo se ejecute durante unos minutos más. Podemos ver que lleva un tiempo, pero es capaz de limpiar las devoluciones de llamada.

ingrese la descripción de la imagen aquí

En resumen, al menos en los navegadores modernos, no tiene que preocuparse por las promesas no resueltas siempre que no tenga referencias externas a ellas.

Benjamin Gruenbaum
fuente
7
¿No significaría esto que si una promesa tarda demasiado en resolverse (pero finalmente se resolvería), corre el riesgo de ser rechazada?
w.brian
5
@ w.brian a menos que lo asigne en algún lugar, por ejemplo, a una variable: var b = $http.get(...)o le agregue una devolución de llamada. Eso también tiene una referencia a eso. Si algo lo resuelve (como dijiste, demasiado tiempo para resolver aún significa resolver), tiene que tener una referencia. Entonces sí, no será GC'd
Benjamin Gruenbaum
3
Entendido, eso es lo que pensé. Entonces, la pregunta es "¿Las promesas nunca resueltas causan pérdida de memoria?" La respuesta, para el caso de uso común en el que se pasa una devolución de llamada a la promesa, es sí. Esta línea en su respuesta parece contradecir eso: "También podemos ver algunos patrones interesantes de GC si dejamos que su último ejemplo se ejecute durante unos minutos más. Podemos ver que lleva un tiempo, pero es capaz de limpiar las devoluciones de llamada. " Lo siento si estoy siendo pedante y quisquilloso, solo estoy tratando de asegurarme de que entiendo esto.
w.brian
1
Eso no parece tener sentido para mí. Si hubiera creado 100.000 promesas, console.log () 'editó alguna línea. Me gustaría que esos 100.000 registraran esas líneas si de repente se resuelven con algo de magia. ¿O está diciendo que el navegador sabrá que esto nunca se resolverá, ya que ni yo ni el navegador real tenemos ninguna referencia al mismo (nada lo afecta)? Entonces, ¿cómo podría ser cierto? (hmm, puedo ver que podría ser cierto)
odinho - Velmont
8
Hay algo de verdad en estos comentarios y algo engañoso, así que déjame aclarar: una promesa con controladores adjuntos podría ser elegible para la recolección de basura. Una promesa se mantiene viva (no elegible para GC) si alguna de las siguientes condiciones es cierta: (1) hay una referencia al objeto de la promesa, (2) hay una referencia al estado "diferido" de la promesa (el objeto / funciones que utiliza para resolverlo / rechazarlo). Fuera de esto, la promesa es elegible para GC. (Si nadie tiene la promesa y nadie puede cambiar su estado, ¿cuál es su propósito, de todos modos?)
cdhowie