Никогда не выполненное обещание вызывает утечку памяти?

92

У меня есть Promise. Я создал его, чтобы при необходимости отменить AJAX-запрос. Но поскольку мне не нужно отменять этот AJAX, я никогда не разрешал его, и AJAX успешно завершился.

Упрощенный фрагмент:

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?

Неужели никогда не выполненные обещания вызывают утечку памяти? Есть ли у вас какие-либо советы по управлению Promiseжизненным циклом?

Умут Бензер
источник
4
«Никогда не выполненное» обещание может быть «отклонено». Слово, которое вы искали, было «невыполнено».
Стивен Вашон
$ http - интересный пример, потому что в конечном итоге HTTP-запрос будет истекать по таймауту (или иначе выдает ошибку), если клиент не может связаться с сервером, независимо от обещания, переданного аргументу «тайм-аут».
Райанвебджексон

Ответы:

145

Что ж, я предполагаю, что вы не сохраняете явную ссылку на него, поскольку это заставит его оставаться выделенным.

Самый простой тест, который я мог придумать, - это выделить много обещаний, а не разрешить их:

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

А потом смотрю саму кучу. Как мы видим в инструментах профилирования Chrome, при этом накапливается необходимая память для выделения 100 обещаний, а затем просто «остается там» менее 15 мегабайт для всей страницы JSFIddle.

введите описание изображения здесь

С другой стороны, если мы посмотрим на $qисходный код

Мы можем видеть, что нет ссылки из глобальной точки на какое-либо конкретное обещание, а есть только от обещания к его обратным вызовам. Код очень читаемый и понятный. Давайте посмотрим, что, если у вас есть ссылка из обратного вызова на обещание.

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

введите описание изображения здесь

Итак, после первоначального распределения - похоже, что он тоже может справиться с этим :)

Мы также можем увидеть некоторые интересные паттерны GC, если мы позволим его последнему примеру поработать еще несколько минут. Мы видим, что это занимает некоторое время, но оно может очистить обратные вызовы.

введите описание изображения здесь

Вкратце - по крайней мере, в современных браузерах - вам не нужно беспокоиться о неразрешенных обещаниях, если у вас нет внешних ссылок на них.

Бенджамин Грюнбаум
источник
8
Разве это не означало бы, что если обещание выполняется слишком долго (но в конечном итоге выполняется), оно рискует попасть в сборку мусора?
w.brian
6
@ w.brian, если вы не назначили его где-нибудь - например, переменной: var b = $http.get(...)или не добавили к нему обратный вызов. Это также имеет ссылку на это. Если что-то разрешает это (как вы сказали - слишком долго, чтобы разрешить, все еще означает разрешение), у него должна быть ссылка на это. Так что да - это не будет GC'd
Benjamin Gruenbaum
3
Попался, вот что я подумал. Итак, вопрос: «Неужели никогда не выполненные обещания вызывают утечку памяти?» Ответ для распространенного варианта использования, когда в обещание передается обратный вызов, - да. Эта строка в вашем ответе, кажется, противоречит следующему: «Мы также можем увидеть некоторые интересные паттерны GC, если мы позволим его последнему примеру поработать еще несколько минут. Мы видим, что это требует времени - но он может очистить обратные вызовы. " Извините, если я педантичен и придирчив, я просто пытаюсь убедиться, что понимаю это.
w.brian
1
Мне это кажется бессмысленным. Если бы я создал 100000 обещаний, в console.log () была бы какая-то строка. Я бы хотел, чтобы эти 100000 записали эти строки, если они вдруг разрешатся каким-то волшебством. Или вы говорите, что браузер будет знать, что это никогда не разрешится, поскольку ни я ни сам браузер не имеют на него никаких ссылок (ничто не влияет на это) - так как же это могло быть правдой? (хм, я вижу, что это может быть правдой)
odinho - Velmont
9
В этих комментариях есть доля правды, а некоторые вводят в заблуждение, поэтому позвольте мне уточнить: обещание с прикрепленными обработчиками может иметь право на сборку мусора. Обещание остается активным (не пригодным для сборки мусора), если выполняется одно из следующих условий: (1) есть ссылка на объект обещания, (2) есть ссылка на «отложенное» состояние обещания (объект / functions, которые вы используете для разрешения / отклонения). В остальном обещание имеет право на сборку мусора. (Если ни у кого нет обещания и никто не может изменить его состояние, в любом случае, какова его цель?)
cdhowie