Предотвращение длительной работы javascript от блокировки браузера


У меня есть JavaScript, который выполняет множество вычислений, а также чтение/запись значений из/в DOM. Страница огромна, поэтому это часто заканчивается блокировкой браузера до минуты (иногда дольше с IE) с использованием 100% процессора.

есть ли какие-либо ресурсы по оптимизации JavaScript, чтобы предотвратить это (все, что я могу найти, это как отключить длительное предупреждение сценария Firefox)?

7 52

7 ответов:

Если вы можете превратить свой алгоритм расчета в то, что можно назвать итеративно, вы можете освободить управление обратно в браузер с частыми интервалами с помощью setTimeout С коротким таймаутом.

например, что-то вроде этого...

function doCalculation()
{
   //do your thing for a short time

   //figure out how complete you are
   var percent_complete=....

   return percent_complete;
}

function pump()
{
   var percent_complete=doCalculation();

   //maybe update a progress meter here!

   //carry on pumping?
   if (percent_complete<100)
   {
      setTimeout(pump, 50);
   }
}

//start the calculation
pump();

использовать тайм-ауты.

помещая содержимое вашего цикла (ов) в отдельные функции и вызывая их из setTimeout () с таймаутом 50 или около того, javascript даст контроль над потоком и вернется через некоторое время, позволяя пользовательскому интерфейсу получить внешний вид.

есть хорошее прохождение здесь.

Я вел блог о в браузере производительность некоторое время назад, но позвольте мне обобщить те, которые связаны с DOM для вас здесь.

  • обновляйте DOM как можно реже. Внесите изменения в объекты DOM в памяти и добавьте их только один раз в DOM.
  • используйте innerHTML. Это быстрее, чем методы DOM в большинстве браузеров.
  • используйте делегирование событий вместо обычной обработки событий.
  • знать, какие звонки стоят дорого, и избегать их. Например, в jQuery, $("div.className") будет дороже, чем $("#someId").

тогда есть некоторые, связанные с самим JavaScript:

  • петля как можно меньше. Если у вас есть одна функция, которая собирает узлы DOM, и другая, которая их обрабатывает, вы выполняете цикл дважды. Вместо этого передайте анонимную функцию функции, которая собирает узлы, и обработайте узлы по мере их сбора.
  • использовать родной функции, когда это возможно. Например, итераторы forEach.
  • используйте setTimeout, чтобы позволить браузеру дышать время от времени.
  • для дорогих функций, которые имеют идемпотентные выходы, кэшировать результаты, так что вам не придется пересчитывать его.

есть еще несколько в моем блоге (ссылка выше).

Это все еще немного кровоточащий край, но Firefox 3.5 имеет эти вещи, называемые веб-работниками, я не уверен в их поддержке в других браузерах.

У мистера отставки есть статья о них здесь:http://ejohn.org/blog/web-workers/

и Имитация Отжига вероятно, самый простой пример этого, если вы заметите, что вращающийся логотип Firefox не замерзает, когда рабочие потоки выполняют свои запросы (таким образом, нет замораживание браузера).

вы можете попробовать выполнить длительные вычисления в потоках (см. JavaScript и нити), хотя они не очень портативный.

вы также можете попробовать использовать некоторые профилировщик Javascript, чтобы найти узкие места производительности. Firebug поддерживает профилирование javascript.

мой опыт заключается в том, что манипуляция DOM, особенно в IE, гораздо более важна для производительности, чем "основной" JavaScript (цикл и т. д.).

Если вы строите узлы, в IE это намного быстрее сделать, создав строку HTML, а затем установив innerHTML в контейнере, чем с помощью методов DOM, таких как createElement/appendChild.

вы можете попробовать сократить код на

 $(xmlDoc).find("Object").each(function(arg1) {
    (function(arg1_received) {
                setTimeout(function(arg1_received_reached) {

                    //your stuff with the arg1_received_reached goes here 

                }(arg1_received), 0)
            })(arg1)
}(this));

или для циклов" для " попробуйте

for (var i = 0 ; i < 10000 ; i = i + 1) {
    (function(arg1_received) {
        setTimeout(function(arg1_received_reached) {

            //your stuff with the arg1_received_reached goes here 

        }(arg1_received), 0)
    })(arg1_to_send)
}

у меня была та же проблема, и мои клиенты сообщали об этом как об ошибке "убить страницу". Но теперь у меня есть лучшее решение для этого. :)