Czy obietnica nigdy nie rozwiązana powoduje wyciek pamięci?

92

Mam Promise. Utworzyłem go, aby w razie potrzeby anulować żądanie AJAX. Ale ponieważ nie muszę anulować tego AJAX, nigdy go nie rozwiązałem i AJAX zakończył się pomyślnie.

Uproszczony fragment:

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?

Czy nigdy nie rozwiązane takie obietnice powodują wycieki pamięci? Czy masz jakieś rady dotyczące zarządzania Promisecyklem życia?

Umut Benzer
źródło
4
Obietnica „nigdy nie rozwiązana” nadal może zostać „odrzucona”. Słowo, którego szukałeś, było „niespełnione”.
Steven Vachon
$ http jest interesującym przykładem, ponieważ ostatecznie żądanie HTTP przekroczy limit czasu (lub w inny sposób zwróci odpowiedź o błędzie), jeśli klient nie może połączyć się z serwerem, niezależnie od obietnicy przekazanej argumentowi „timeout”.
ryanwebjackson

Odpowiedzi:

145

Cóż, zakładam, że nie masz do niego wyraźnego odniesienia, ponieważ zmusiłoby to go do pozostania przydzielonym.

Najprostszym testem, jaki przychodzi mi do głowy, jest przydzielanie wielu obietnic, a nie ich rozwiązywanie:

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

A potem oglądanie samej sterty. Jak widać w narzędziach do profilowania Chrome, gromadzi to pamięć potrzebną do przydzielenia 100 obietnic, a następnie po prostu „pozostaje tam” przy mniej niż 15 megabajtach dla całej strony JSFIddle

wprowadź opis obrazu tutaj

Z drugiej strony, jeśli spojrzymy na $qkod źródłowy

Widzimy, że nie ma odniesienia z punktu globalnego do żadnej konkretnej obietnicy, ale tylko z obietnicy do jej wywołań zwrotnych. Kod jest bardzo czytelny i przejrzysty. Zobaczmy, co się stanie, jeśli jednak masz odniesienie z wywołania zwrotnego do obietnicy.

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

wprowadź opis obrazu tutaj

Więc po wstępnej alokacji - wygląda na to, że sobie z tym poradzi :)

Możemy również zobaczyć kilka interesujących wzorów GC, jeśli pozwolimy, aby jego ostatni przykład działał jeszcze przez kilka minut. Widzimy, że zajmuje to trochę czasu - ale jest w stanie wyczyścić wywołania zwrotne.

wprowadź opis obrazu tutaj

Krótko mówiąc - przynajmniej w nowoczesnych przeglądarkach - nie musisz się martwić o nierozwiązane obietnice, o ile nie masz do nich zewnętrznych odniesień

Benjamin Gruenbaum
źródło
8
Czy nie oznaczałoby to, że jeśli obietnica zajmie zbyt dużo czasu (ale ostatecznie zostanie rozwiązana), istnieje ryzyko, że zostanie ona poddana GC?
w.brian
6
@ w.brian, chyba że gdzieś go przypiszesz - na przykład do zmiennej: var b = $http.get(...)lub dodasz do niej callback. To również ma odniesienie do tego. Jeśli coś go rozwiązuje (tak jak powiedziałeś - zbyt długi czas na rozwiązanie nadal oznacza rozwiązanie) - musi mieć do tego odniesienie. Więc tak - to nie będzie GC'd
Benjamin
3
Mam cię, tak myślałem. Tak więc pytanie brzmi: „Czy nigdy nie rozwiązane obietnice powodują wyciek pamięci?” Odpowiedź na typowy przypadek użycia, w którym wywołanie zwrotne jest przekazywane do obietnicy, brzmi tak. Ten wers w Twojej odpowiedzi wydaje się zaprzeczać temu: „Możemy również zobaczyć kilka interesujących wzorców GC, jeśli pozwolimy, aby jego ostatni przykład działał jeszcze przez kilka minut. Widzimy, że zajmuje to trochę czasu - ale jest w stanie wyczyścić wywołania zwrotne. " Przepraszam, jeśli jestem pedantyczny i wybredny, staram się tylko upewnić, że to rozumiem.
w.brian
1
Wydaje mi się, że to nie ma dla mnie sensu. Gdybym utworzył 100.000 obietnic, to console.log () 'wydała jakąś linię. Chciałbym, aby te 100 000 zapisywało te linie, jeśli nagle zostaną rozwiązane za pomocą jakiejś magii. A może mówisz, że przeglądarka będzie wiedziała, że ​​to się nigdy nie rozwiąże, ponieważ ani ja, ani aktualna przeglądarka nie mają do niego żadnego odniesienia (nic na to nie ma wpływu) - więc jak to może być prawdą? (hmm, widzę, że to może być prawda)
odinho - Velmont
9
W tych komentarzach jest trochę prawdy, a niektóre wprowadzają w błąd, więc pozwól mi wyjaśnić: obietnica z dołączonymi programami obsługi może kwalifikować się do czyszczenia pamięci. Obietnica jest utrzymywana przy życiu (nie kwalifikuje się do GC), jeśli którykolwiek z poniższych warunków jest prawdziwy: (1) istnieje odniesienie do obiektu obietnicy, (2) istnieje odniesienie do „odroczonego” stanu obietnicy (obiekt / funkcje, których używasz do rozwiązania / odrzucenia tego problemu). Poza tym obietnica kwalifikuje się do GC. (Jeśli nikt nie ma obietnicy i nikt nie może zmienić jej stanu, zresztą jaki jest już jej cel?)
cdhowie