Подтвердить что ты не робот

Как остановить интенсивный цикл Javascript от замораживания браузера

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

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

Ответ 1

Я бы выбрал "каждую" функцию в пользу цикла 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");

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

Ответ 2

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

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

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

  //process element;

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

processing();

Ответ 3

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

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

Ответ 4

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

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

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

Ответ 5

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

Ответ 6

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

Ответ 7

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

Ответ 8

У меня была та же проблема, что и при повторном обновлении страницы. Причиной было два вложенных для петель, которые произошли более чем в 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. Используйте ту же концепцию у работников, поскольку они не заставляют ваш браузер замораживаться, потому что они работают на фоне основного потока. Если просто применить это с API Workers не получилось, поместите каждый из экземпляров loopForSubset у разных работников и объедините результат внутри основного вызывающего пользователя Worker.

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

Ответ 9

Вы можете попытаться сократить код

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

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

Ответ 10

В качестве модификации @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();