Как остановить интенсивный цикл Javascript от замораживания браузера
Я использую Javascript для анализа XML файла с примерно 3500 элементами. Я использую функцию "каждый" jQuery, но я мог бы использовать любую форму цикла.
Проблема в том, что браузер замораживается на несколько секунд, пока цикл выполняется. Какой лучший способ остановить замораживание браузера, не замедляя слишком много кода?
$(xmlDoc).find("Object").each(function() {
//Processing here
});
Ответы
Ответ 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();