我正在使用Javascript解析一个包含大约3,500个元素的XML文件。我正在使用jQuery“each”函数,但我可以使用任何形式的循环 问题是浏览器在循环执行时冻结了几秒钟。在不降低代码速度的情况下停止冻结浏览器的最佳方法是什么?
$(xmlDoc).find("Object").each(function() {
//Processing here
});
答案 0 :(得分:68)
我会抛弃“each”函数,转而使用for循环,因为它更快。我还会使用“setTimeout”添加一些等待,但只是每隔一段时间,只有在需要时。您不希望每次等待5毫秒,因为处理3500条记录大约需要17.5秒。
下面是一个使用for循环的示例,它以5 ms的间隔处理100条记录(您可以调整它),这会产生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处理的不同部分进行基准测试,看看是否存在可能修复的瓶颈。您可以使用firebug的分析器在firefox中进行基准测试,并像这样写入控制台:
// start benchmark
var t = new Date();
// some xml processing
console.log("Time to process: " + new Date() - t + "ms");
希望这有帮助。
答案 1 :(得分:22)
在处理之间设置timeOut以防止循环周期占用所有浏览器资源。总的来说,处理和循环所有内容只需要几秒钟,而不是3,500个元素的不合理。
var xmlElements = $(xmlDoc).find('Object');
var processing = function() {
var element = xmlElements.shift();
//process element;
if (xmlElements.length > 0) {
setTimeout(processing, 5);
}
}
processing();
答案 2 :(得分:6)
我会考虑将3500个元素从xml转换为JSON服务器端,或者甚至更好地将其上传到服务器转换,以便它来自getgo的原生JS。
这样可以最大限度地减少负载,并且可以大大减小文件大小。
答案 3 :(得分:3)
使用Turboid框架可以在不冻结浏览器的情况下进行长循环。有了它,您可以编写如下代码:
loop(function(){
// Do something...
}, number_of_iterations, number_of_milliseconds);
此turboid.net文章中的更多详细信息:Real loops in Javascript
答案 4 :(得分:2)
Javascript是单线程的,所以除了setTimeout
之外,你无能为力。如果使用Google Gears是您网站的一个选项,它们可以在真正的后台线程中运行javascript。
答案 5 :(得分:2)
你可以使用持续时间为零的setTimeout()来生成所需的
答案 6 :(得分:1)
您可以使用HTML5工作者API,但这只适用于Firefox 3.1和Safari 4测试版。
答案 7 :(得分:1)
当用户连续刷新页面时,我遇到了同样的问题。原因是两个嵌套for循环,发生超过52000次。 Firefox 24中的这个问题比Chrome 29更严重,因为Firefox会更快崩溃(比Chrome早约2000毫秒)。我简单地做了,它的工作原理是我使用“for”循环而不是每个循环,然后我重构代码,以便将整个循环数组分成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
}
}
另一个对我有用的解决方案是使用HTML5中的Worker APIs
实现的相同解决方案。在工作程序中使用相同的概念,因为它们可以在主线程的后台运行,从而避免冻结浏览器。如果只使用Workers API应用它,则将loopForSubset
的每个实例放在不同的worker中,并将结果合并到Worker的主调用者中。
我的意思是这可能不完美,但这已经奏效了。如果有人仍然认为这可能适合他们,我可以提供更多真正的代码块。
答案 8 :(得分:1)
您可以尝试通过
缩短代码 $(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));
这不会对你造成太大伤害;)
答案 9 :(得分:0)
作为@ 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();