2016-01-08 1 views
0

ОБНОВЛЕНИЕ: проблема загадочно исчезла после длинной сессии jslint, поэтому я должен сделать вывод, что это была какая-то глупая ошибка где-то в моем коде, которая была исправлена удалив некоторые, по-видимому, безобидные опечатки.AJAX вызов кеширования через Promise делает использование браузера CPU (и память утечки)

Первоначальной проблема

У меня есть страница, которая загружает значительное (и случайно переменное) количество объектов AJAX с помощью JQuery .post вызовов.

Так что я бы генерировать много вызовов, таких как

/entry/get/orange 
/entry/get/red 
/entry/get/yellow 
/entry/get/red  <--- duplicate! 
/entry/get/red  <--- again! 
/entry/get/yellow 
/entry/get/red  <--- dude, what's wrong with you? 
... 

При работе таким образом, что все работает как надо, помимо того, что на самом деле, очень неэффективно, и попав на сервер довольно понапрасну ,

попытка решения

Так что я перешел на кэширование через Promise, как это:

// I have a global colorCache variable 

function retrieve(color, callback) { 
    console.log("retrieving " + color); 
    var found = -1; 
    // Each entry in colorCache is an object with some other values 
    for (var i in colorCache) { 
     if (colorCache[i].color === color) { 
      found = i; 
      break; 
     } 
    } 
    if (found < 0) { 
     console.log("effecting call for " + color); 
     var url = "blah/blah/blah/" + color; 
     var ppar = { 
      foo: "bar", 
      answer: 42 
     }; 
     /* push returns the new length of colorCache */ 
     found = colorCache.push({ 
      color : color, 
      promise : $.post(url, ppar), 
      ts  : new Date().getTime() 
     }) - 1; 
    } else { 
     console.log("cache hit for " + color); 
    } 
    // now found is valid. Return the Promise too, for appending. 
    return colorCache[found].success(callback); 
} 

Таким образом, если соответствующий .post уже выдан, обратный вызов снова вызывается с теми же данными, является ли сам вызов уже завершен или все еще продолжается.

Снова все работает. Видимо.

Теперь у меня есть другого проблема

С этой новой версией,

  • страница загружается
  • все объекты загружаются (намного быстрее, чем раньше, - как и следовало ожидать)
  • страницы останавливается, все еще ... нет анимаций, нет часов, ничего ...
  • ... но Process Explorer теперь показывает Firefox постоянно ест 26-30% от CPU. И будет продолжить, чтобы сделать это до тех пор, пока я не закрою вкладку, на которой загружена страница. Кроме того, Потребление памяти в Firefox постоянно растет. Я наблюдал, как он поднимался с 800 м до 1,7 ГБ за чуть меньше часа, а не полностью регулярно, но с заметным восходящим трендом.

В чем проблема?

Важное примечание: есть несколько «утечка браузера» вопросы и ответы на переполнение стека, но все они связаны повторил JQuery вызовы, выделять объекты или перебирать в некотором роде. Здесь, как только я запустил пятьдесят вызовов jQuery, ничего больше не происходит. не

То, что я пытался (до сих пор)

  • Я проверил с Firebug: нет консольного предупреждения или ошибок, не сигнальных журналов (все функций я называю есть), никакой сетевой активность (двойная - проверяется с сервера сервера Apache).
  • Я просмотрел процесс Firefox в памяти; основной поток запущен, но я не очень хорошо разбираюсь в потоковой информации Process Explorer - он показывает что-то о «forInIterator», которое мало мне подсказывает.
  • Отсутствует активность, за исключением процессора (сеть, чтение/запись реестра Windows).
  • Я немного искал Google, и нашел something возможно связанный (но, возможно, нет). Я еще не пробовал это предложение, но я сделаю это в ближайшее время, даже если я не вижу, как он может помочь, и в любом случае я не использую when.js, даже если что-то подобное может заполнить jQuery с: Одним из возможных способов обхода является обеспечение того, что ваши обратные вызовы и возвратные функции возвращают что-то (кроме $ .Deferred).
  • завернул вызов .post с помощью Promise.resolve, как предложил Jaromanda X, и переместил объект кеша внутри функции, чтобы избежать загрязнения глобальной области (что всегда является хорошей практикой).
  • Вернул непосредственно объект Promise и вызвал обратный вызов путем добавления внешнего. Then.
  • Протестировано с помощью Chrome. Те же результаты (в некотором смысле это обнадеживающие), за исключением того, что загрузка процессора постоянна на 17%.
  • Закрытие вкладки делает загрузку процессора выключенной.
+0

Вам не нужно: return colorCache [found] .promise.then (callback); '? 'colorCache [found]' - это ваш объект, который не имеет метода '.success()', который вы пытались вызвать. Кроме того, я переключился на '.then()', чтобы быть намного более совместимым с будущим, поскольку '.success()' нестандартен. – jfriend00

+0

Я читал * jQuery.deferred * не совсем Promise/A + spec - это не значит, что это источник этой проблемы, однако попробуйте «обернуть» '.post (url, ppar)' в native 'Promise.resolve ($. Post (url, ppar))' и посмотреть, не исчезла ли проблема использования процессора - также вот [скрипка] (https://jsfiddle.net/6qr4rx65/) вашей функции извлечения, которая не работает 't нужна глобальная переменная и упрощает (я думаю) логику (нет для циклов для поиска кешированных значений, например) - скрипка обертывает jQuery, как указано в этом комментарии –

+0

@JaromandaX, я пробовал, но не работал , Теперь я попытаюсь уменьшить количество вызовов и посмотреть, не потеряет ли это ключ. – LSerni

ответ

0

TL; DR Проблема не непосредственно связанные с кэшированием на всех. Кэширование, а точнее: более высокий вызов, разрешенный кешированием, похоже, вызывает недоразумение где-то еще в отношениях jQuery/bootstrapSwitch. Возможно также, что я неправильно использовал bootstrapSwitch.


отладка и обнаружение

Поскольку нет никаких запросов XHR работает, проблема процессора должна быть вызвана либо коды зацикливания явно или неявно делать это (например setInterval).

Инструментирование функции таймера в браузере

window.timers = {}; 
originalSetTimeout = window.setTimeout; 
originalSetInterval = window.setInterval; 
window.setTimeout = function(fu, t) { 
    var id = originalSetTimeout(function() { 
     console.log(id+" has timed out"); 
     delete window.timers[id]; // do not track popped timers 
     fu(); 
    }, t); 
    window.timers[id] = {id:id, type: 'timeout', setAt: new Date(), timeout: t }; 
    console.log(id+" has been set to pop in "+t+"ms"); 
} 

window.setInterval = function(fu, t) { 
    var id = originalSetInterval(function() { 
     window.timers[id].fired++; 
     fu(); 
    }, t); 
    window.timers[id] = {id:id, type: 'interval', setAt: new Date(), timeout: t, fired: 0 }; 
    console.log(id+" has been set to pop every "+t+"ms"); 
} 

показывает, что есть несколько таймеров, работающих непрерывно.

Исследуя таймеры

Код является «строгим», но после того, как конкретный таймер известно, я могу выбросить исключение в моем setInterval, и быть в состоянии проверить стек вызовов.

Так получается, что виновник этого кода (удаление это делает проблему исчезнуть):

.bootstrapSwitch({ 
     onSwitchChange: function(e, state) { 
      ... 
     } 
    }) 

Заключение

Причина проблемы заключается в том, что у меня есть довольно много флажков в результирующая страница: они украшены bootstrapSwitch, но они начинаются с скрытых. И в этом случае bootstrapSwitch запускает таймер, который ожидает, что флажок будет отображаться, прежде чем обогащать его DOM. По-видимому, это (один из?) выполняет следующие функции:

return initInterval = window.setInterval((function(_this) { 
     return function() { 
     if (_this.$wrapper.is(":visible")) { 
      init(); 
      return window.clearInterval(initInterval); 
     } 
     }; 
    })(this), 50); 

Обычно эти функции не загружают ЦП значительно. Но по непонятным причинам большое (40+) количество запросов AJAX за короткое время, некоторые из которых вызывают bootstrapSwitch, делают вышеперечисленные таймеры генерируют гораздо более высокую нагрузку.

Принудительные переключатели галочки показывают друг за другом, скрывая их сразу же после этого, а также несколько работает: осталось два таймера с загрузкой процессора ниже 5%, но все остальные правильно завершаются. Флажок флажка неловкий.

И наконец, я написал одну функцию таймера, которая будет откладывать бутстрапSwitch, пока соответствующие флажки не будут действительно заметны. Как только они появляются, bootstrapSwitch вызывается, но по мере их видимости дополнительный таймер не создается. Таким образом, все, что происходит в недрах таймера, никогда не запускается.

Смежные вопросы