A promessa nunca resolvida causa vazamento de memória?

91

Eu tenho um Promise. Eu o criei para cancelar uma solicitação AJAX, se necessário. Mas como não preciso cancelar aquele AJAX, nunca resolvi e o AJAX foi concluído com êxito.

Um snippet 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?

Promessas nunca resolvidas como essa causam vazamentos de memória? Você tem algum conselho sobre como gerenciar o Promiseciclo de vida?

Umut Benzer
fonte
4
Uma promessa "nunca resolvida" ainda pode ser "rejeitada". A palavra que você procurava "não cumprida".
Steven Vachon
$ http é um exemplo interessante porque eventualmente uma solicitação HTTP irá expirar (ou de outra forma produzir uma resposta de erro), se o cliente não puder alcançar o servidor, independentemente da promessa passada para o argumento 'timeout'.
ryanwebjackson

Respostas:

144

Bem, presumo que você não mantenha uma referência explícita a ele, pois isso o forçaria a permanecer alocado.

O teste mais simples que eu poderia pensar é realmente alocar um monte de promessas e não resolvê-las:

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

E então observando a própria pilha. Como podemos ver nas ferramentas de criação de perfil do Chrome, isso acumula a memória necessária para alocar 100 promessas e então apenas "permanece lá" com menos de 15 megabyes para toda a página JSFIddle

insira a descrição da imagem aqui

Por outro lado, se olharmos para o $qcódigo-fonte

Podemos ver que não há referência de um ponto global a qualquer promessa em particular, mas apenas de uma promessa a seus retornos de chamada. O código é muito legível e claro. Vamos ver se você, entretanto, tem uma referência do retorno de chamada para a promessa.

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

insira a descrição da imagem aqui

Portanto, após a alocação inicial - parece que também é capaz de lidar com isso :)

Também podemos ver alguns padrões interessantes de GC se deixarmos seu último exemplo rodar por mais alguns minutos. Podemos ver que demora um pouco - mas é capaz de limpar os callbacks.

insira a descrição da imagem aqui

Resumindo - pelo menos nos navegadores modernos - você não precisa se preocupar com promessas não resolvidas, desde que não tenha referências externas a elas

Benjamin Gruenbaum
fonte
7
Isso não significa que, se uma promessa demorar muito para ser resolvida (mas acabaria por ser resolvida), corre o risco de ser GC?
w.brian
5
@ w.brian a menos que você o atribua em algum lugar - por exemplo, a uma variável: var b = $http.get(...)ou adicione um callback a ela. Isso também tem uma referência a isso. Se algo resolve (como você disse - muito tempo para resolver ainda significa resolver) - tem que ter uma referência a isso. Então, sim - não será GC'd
Benjamin Gruenbaum
3
Te peguei, foi o que pensei. Portanto, a pergunta é "Promessas nunca resolvidas causam vazamento de memória?" A resposta, para o caso de uso comum em que um retorno de chamada é passado para a promessa, é sim. Esta linha em sua resposta parece contradizer que: "Também podemos ver alguns padrões interessantes de GC se deixarmos seu último exemplo rodar por mais alguns minutos. Podemos ver que leva um tempo - mas é capaz de limpar os retornos de chamada. " Desculpe se estou sendo pedante e minucioso, só estou tentando ter certeza de que entendi isso.
w.brian
1
Isso não parece fazer sentido para mim. Se eu tivesse criado 100.000 promessas que console.log () 'ed alguma linha. Eu gostaria que esses 100.000 registrassem essas linhas se de repente eles resolverem por um pouco de mágica. Ou você está dizendo que o navegador saberá que isso nunca resolverá, já que nem eu nem o navegador real temos qualquer referência a ele (nada afeta isso) - então, como isso poderia ser verdade? (hmm, posso ver que pode ser verdade)
odinho - Velmont
8
Há alguma verdade nesses comentários e algumas que são enganosas, então deixe-me esclarecer: uma promessa com manipuladores anexados pode ser elegível para a coleta de lixo. Uma promessa é mantida viva (não elegível para GC) se qualquer uma das seguintes opções for verdadeira: (1) há uma referência ao objeto da promessa, (2) há uma referência ao estado "diferido" da promessa (o objeto / funções que você usa para resolver / rejeitar). Fora disso, a promessa é elegível para GC. (Se ninguém tem a promessa e ninguém pode mudar seu estado, qual é o seu propósito mais, afinal?)
cdhowie