Чи ніколи не вирішена обіцянка спричиняє витік пам'яті?


91

У мене є 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 вичерпає час очікування (або іншим чином дасть відповідь на помилку), якщо клієнт не може отримати доступ до сервера, незалежно від обіцянки, переданої аргументу 'timeout'.
ryanwebjackson

Відповіді:


144

Ну, я припускаю, що ви не зберігаєте явного посилання на нього, оскільки це змусить його залишатися розподіленим.

Найпростіший тест, про який я міг подумати, насправді розподіляє багато обіцянок, а не вирішує їх:

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, якщо дозволити його останньому прикладу працювати ще кілька хвилин. Ми бачимо, що це займає деякий час - але це здатне очистити зворотні дзвінки.

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

Коротше кажучи - принаймні в сучасних браузерах - вам не доведеться турбуватися про невирішені обіцянки, якщо у вас немає зовнішніх посилань на них


7
Чи не буде це означати, що якщо обіцянка займає занадто багато часу (але врешті-решт вирішить), вона ризикує бути GC'd?
w.brian

5
@ w.brian, якщо ви не призначите його десь - наприклад, змінній: var b = $http.get(...)або не додайте до нього зворотний виклик. Це також має посилання на це. Якщо щось вирішує (як ви сказали - занадто довго, щоб вирішити, все-таки означає вирішити) - воно повинно мати на це посилання. Так що так - це не буде GC'd
Бенджамін

3
Боже, ось що я думав. Отже, запитання: "Чи ніколи не вирішені обіцянки спричиняють витік пам'яті?" Відповідь на поширений випадок використання, коли зворотний виклик передається обіцянці, - так. Цей рядок у вашій відповіді суперечить такому: "Ми також можемо побачити деякі цікаві закономірності GC, якщо дозволити його останньому прикладу працювати ще кілька хвилин. Ми можемо бачити, що це займає деякий час - але він може очистити зворотні дзвінки. " Вибачте, якщо я педантичний і прискіпливий, я просто намагаюся переконатись, що це розумію.
w.brian

1
Здається, це не має для мене сенсу. Якби я створив 100 000 обіцянок, то console.log () видав якийсь рядок. Я хотів би, щоб ці 100 000 записали ці рядки, якщо вони раптом вирішились якоюсь магією. Або ви хочете сказати, що браузер знатиме, що це ніколи не вирішить, оскільки ні я, ні справжній браузер не маємо до нього жодних посилань (нічого не впливає на це) - так як це могло бути правдою? (Хм, я бачу, що це може бути правдою)
Одінью - Вельмонт

8
У цих коментарях є певна правда, яка вводить в оману, тож дозвольте пояснити: обіцянка з приєднаними обробниками може мати право на вивіз сміття. Обіцянка залишається в живих (не підходить для GC), якщо є будь-що з наведеного нижче: (1) є посилання на об'єкт обіцянки, (2) є посилання на "відкладений" стан обіцянки (об'єкт / функції, які ви використовуєте для вирішення / відхилення). Поза цим, обіцянка має право на GC. (Якщо ніхто не обіцяє, і ніхто не може змінити його стан, яка ціль його більше, у будь-якому разі?)
cdhowie
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.