如何阻止激烈的JavaScript循环冻结浏览器
我正在使用Javascriptparsing一个约3500个元素的XML文件。 我正在使用jQuery“each”函数,但我可以使用任何forms的循环。
问题在于循环执行时浏览器冻结了几秒钟。 停止冻结浏览器的最佳方法是什么?
$(xmlDoc).find("Object").each(function() { //Processing here });
因为速度更快,所以我会抛弃“each”函数来支持for循环。 我还会使用“setTimeout”添加一些等待,但是只有在经常需要的时候才会这样。 您不希望每次等待5ms,因为处理3500条logging需要大约17.5秒。
下面是一个使用for循环的示例,它以5 ms的间隔处理100条logging(可以对其进行调整),从而给出175 ms的开销。
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处理的不同部分进行基准testing,以查看是否有可能修复的某个瓶颈。 你可以在firefox中使用萤火虫的分析器进行基准testing,并写出如下所示的控制台:
// start benchmark var t = new Date(); // some xml processing console.log("Time to process: " + new Date() - t + "ms");
希望这可以帮助。
在处理之间设置一个超时时间,以防止循环占用所有浏览器资源。 总共只需要几秒钟的时间来处理和循环所有的事情,而不是不合理的3500个元素。
var xmlElements = $(xmlDoc).find('Object'); var processing = function() { var element = xmlElements.shift(); //process element; if (xmlElements.length > 0) { setTimeout(processing, 5); } } processing();
我会考虑将xml中的3500个元素转换为JSON servers,甚至更好地将其上传到服务器转换,以便它从getgo原生的JS。
这将最大限度地减less您的负载,并使文件的大小更小。
使用Turboid框架,可以在不冻结浏览器的情况下实现长循环。 有了它,你可以编写如下代码:
loop(function(){ // Do something... }, number_of_iterations, number_of_milliseconds);
这个turboid.net文章中的更多细节: 在Javascript中的实际循环
Javascript是单线程的,所以除了setTimeout
,没有什么可以做的。 如果使用Google Gears是您的网站的一个选项,它们可以在真正的后台线程中运行JavaScript。
你可以用持续时间为零的setTimeout(),它会根据需要产生
您可以使用HTML5工作程序API,但这只适用于Firefox 3.1和Safari 4 Beta版。
当用户连续刷新页面时,也遇到了同样的问题。 原因是发生了两次以上的嵌套循环,发生了52000次以上。 Firefox 24中的这个问题比Chrome 29更严重,因为Firefox会更快地崩溃(比Chrome快2000年左右)。 我只是做了,它的工作是我用户“for”循环,而不是每个,然后我重构了代码,以便我将整个循环数组分割为4个独立的调用,然后将结果合并为一个。 这个解决scheme已经certificate它已经工作了。
像这样的东西:
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 } }
另一个也适用于我的解决scheme是使用HTML5的Worker APIs
实现的解决scheme。 在工作中使用相同的概念,因为它们避免了浏览器被冻结,因为它们在主线程的后台运行。 如果仅将这个应用于Workers API不起作用,则将loopForSubset
每个实例放在不同的worker中,并将结果合并到Worker的main调用loopForSubset
。
我的意思是这可能不完美,但这已经奏效。 如果有人仍然认为这可能适合他们,我可以帮助更多的真正的代码块。
你可以尝试缩短代码
$(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));
这不会伤害你的;)
作为@ 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++; // eg: 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();