Как остановить интенсивный цикл Javascript от зависания браузера - PullRequest
56 голосов
/ 03 апреля 2009

Я использую Javascript для анализа XML-файла с около 3500 элементов. Я использую jQuery «каждую» функцию, но я мог бы использовать любую форму цикла.
Проблема в том, что браузер зависает на несколько секунд, пока выполняется цикл. Каков наилучший способ остановить зависание браузера, не слишком замедляя код?

$(xmlDoc).find("Object").each(function() {
    //Processing here
});

Ответы [ 10 ]

68 голосов
/ 06 апреля 2009

Я бы отказался от «каждой» функции в пользу цикла for, поскольку он быстрее. Я также добавил бы некоторые ожидания, используя «setTimeout», но только иногда и только при необходимости. Вы не хотите ждать 5 мс каждый раз, потому что тогда обработка 3500 записей займет около 17,5 секунд.

Ниже приведен пример использования цикла for, который обрабатывает 100 записей (вы можете настроить это) с интервалом в 5 мс, что дает издержки в 175 мс.

var xmlElements = $(xmlDoc).find('Object');
var length = xmlElements.length;
var index = 0;
var process = function() {
  for (; index < length; index++) {
    var toProcess = xmlElements[index];
    // Perform xml processing
    if (index + 1 < length && index % 100 == 0) {
        setTimeout(process, 5);
    }
  }
};
process();

Я бы также проверил различные части обработки xml, чтобы увидеть, есть ли где-нибудь узкое место, которое может быть исправлено. Вы можете тестировать в Firefox, используя профилировщик Firebug и записывая в консоль, например:

// start benchmark
var t = new Date();
// some xml processing
console.log("Time to process: " + new Date() - t + "ms");

Надеюсь, это поможет.

22 голосов
/ 03 апреля 2009

Установите интервал между обработками, чтобы цикл цикла не поглотил все ресурсы браузера. В общей сложности для обработки и прохождения всего цикла потребуется всего несколько секунд, что не является необоснованным для 3500 элементов.

var xmlElements = $(xmlDoc).find('Object');

var processing = function() {
  var element = xmlElements.shift();

  //process element;

  if (xmlElements.length > 0) {
    setTimeout(processing, 5);
  }
}

processing();
6 голосов
/ 03 апреля 2009

Я бы посоветовал преобразовать 3500 элементов из xml в серверную часть JSON или даже лучше загрузить их на преобразованный сервер, чтобы они были встроены в JS из getgo.

Это уменьшит вашу нагрузку и уменьшит размер файла.

3 голосов
/ 14 декабря 2011

Длинные циклы без зависания браузера возможны с платформой Turboid. С его помощью вы можете написать код вроде:

loop(function(){  
        // Do something...  
}, number_of_iterations, number_of_milliseconds);

Подробнее в этой статье turboid.net: Реальные циклы в Javascript

2 голосов
/ 04 апреля 2009

вы можете установить Timeout () с длительностью ноль, и он будет давать по желанию

2 голосов
/ 03 апреля 2009

Javascript является однопоточным, поэтому, кроме setTimeout, вы ничего не можете сделать. Если использование Google Gears является опцией для вашего сайта, они предоставляют возможность запуска JavaScript в истинном фоновом потоке.

1 голос
/ 13 июня 2014

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

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

Это не навредит тебе;)

1 голос
/ 04 октября 2013

У меня была та же проблема, которая возникала, когда пользователь последовательно обновлял страницу. Причиной были две вложенные петли, которые происходили более 52000 раз. Эта проблема была более острой в Firefox 24, чем в Chrome 29, поскольку Firefox зависал раньше (примерно на 2000 мс раньше, чем Chrome). Я просто сделал, и это сработало, так как я использовал циклы «для» вместо каждого, а затем я реорганизовал код так, чтобы разделить весь массив циклов на 4 отдельных вызова, а затем объединить результат в один. Это решение доказало, что оно сработало.

Примерно так:

var entittiesToLoop = ["..."]; // Mainly a big array
   loopForSubset(0, firstInterval);
   loopForSubset(firstInterval, secondInterval);
    ...

var loopForSubset = function (startIndex, endIndex) {
    for (var i=startIndex; i < endIndex; i++) {
            //Do your stuff as usual here
    }
}

Другое решение, которое также работало для меня, было тем же решением, реализованным с Worker APIs из HTML5. Используйте ту же концепцию в рабочих, поскольку они избегают зависания вашего браузера, потому что они работают в фоновом режиме вашего основного потока. Если только применение этого с Workers API не сработало, поместите каждый из экземпляров loopForSubset в разных работников и объедините результат в основной вызывающей программе Worker.

Я имею в виду, что это может быть не идеально, но это сработало. Я могу помочь с более реальными кусками кода, если кто-то все еще думает, что это может подойти им.

1 голос
/ 04 апреля 2009

Вы можете использовать рабочий API для HTML5, но он будет работать только в бета-версиях Firefox 3.1 и Safari 4.

0 голосов
/ 17 октября 2015

В качестве модификации @ tj111 ответьте на полный используемый код

    //add pop and shift functions to jQuery library. put in somewhere in your code.
    //pop function is now used here but you can use it in other parts of your code.
    (function( $ ) {
        $.fn.pop = function() {
            var top = this.get(-1);
            this.splice(this.length-1,1);
            return top;
        };

        $.fn.shift = function() {
            var bottom = this.get(0);
            this.splice(0,1);
            return bottom;
        };
    })( jQuery );


//the core of the code:
    var $div = $('body').find('div');//.each();
    var s= $div.length;
    var mIndex = 0;
    var process = function() {
        var $div = $div.first();            
    //here your own code.

    //progress bar:
        mIndex++;
    // e.g.:    progressBar(mIndex/s*100.,$pb0);

    //start new iteration.
        $div.shift();
        if($div.size()>0){
            setTimeout(process, 5);
        } else {
    //when calculations are finished.
            console.log('finished');
        }
    }
    process();
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...