Закрытие и обратная ошибка утечки в javascript - программирование
Подтвердить что ты не робот

Закрытие и обратная ошибка утечки в javascript

function(foo, cb) {
  var bigObject = new BigObject();
  doFoo(foo, function(e) {
     if (e.type === bigObject.type) {
          cb();
          // bigObject = null;
     }
  });
}

В приведенном выше примере показано классическое, случайное (или, возможно, нет) закрытие памяти. Сборщик мусора V8 не может определить, можно ли безопасно удалить bigObject, поскольку он используется в функции обратного вызова, которую можно вызывать несколько раз.

Одним из решений является установка bigObject в null, когда задание в функции обратного вызова завершено. Но если вы используете множество переменных (представьте, что есть n переменные, такие как bigObject, и все они используются в обратном вызове), то очистка становится уродливой проблемой.

Мой вопрос таков: есть ли другой способ очистить используемые переменные?

EDIT Вот еще один пример (реальный мир): поэтому я получаю приложение от mongodb и сравниваю его с каким-либо другим приложением. Обратный вызов от mongodb использует переменное приложение, которое определено из этого обратного вызова. После получения результата от mongodb я возвращаю его также как обратный вызов (потому что это все async, и я не могу просто написать return). Так что на самом деле может случиться так, что я распространяю обратный вызов до самого источника...

function compareApplications(application, condition, callback) {

    var model = database.getModel('Application');
    model.find(condition, function (err, applicationFromMongo) {
        var result = (applicationFromMongo.applicationID == application.applicationID)
        callback(result)        
    }
}
4b9b3361

Ответ 1

Если ваша функция обратного вызова должна быть вызвана один раз, вам следует отказаться от подписки после ее вызова. Это освободит ваш обратный вызов + закрытие GC. С закрытием вашего закрытия, bigObject также будет бесплатным для сбора GC.

Это лучшее решение - как вы отметили, GC не волшебным образом знает, что ваш обратный вызов будет вызываться только один раз.

Ответ 2

Чтобы опираться на ответ Брэндона: если (по какой-то страшной причине) вы не можете отказаться от отзыва своего обратного вызова, вы всегда можете справиться с удалением обратного вызова:

function createSingleUseCallback(callback)
{
    function callbackWrapper()
    {
        var ret = callback.apply(this, arguments);
        delete callback;
        return ret;
    }
    return callbackWrapper;
}

function compareApplications(application, condition, callback)
{
    var model = database.getModel('Application');
    model.find(condition, createSingleUseCallback(function (err, applicationFromMongo)
    {
        var result = (applicationFromMongo.applicationID == application.applicationID);
        callback(result);
    })
}