繁体   English   中英

如何阻止激烈的Javascript循环冻结浏览器

[英]How to stop intense Javascript loop from freezing the browser

我正在使用Javascript解析一个包含大约3,500个元素的XML文件。 我正在使用jQuery“each”函数,但我可以使用任何形式的循环。
问题是浏览器在循环执行时冻结了几秒钟。 在不降低代码速度的情况下停止冻结浏览器的最佳方法是什么?

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

我会抛弃“each”函数,转而使用for循环,因为它更快。 我还会使用“setTimeout”添加一些等待,但只是每隔一段时间,只有在需要时。 您不希望每次等待5毫秒,因为处理3500条记录大约需要17.5秒。

下面是一个使用for循环的示例,它以5 ms的间隔处理100条记录(您可以调整它),这会产生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处理的不同部分进行基准测试,看看是否存在可能修复的瓶颈。 您可以使用firebug的探查器在firefox中进行基准测试,并像这样写入控制台:

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

希望这可以帮助。

在处理之间设置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();

我考虑将3500个元素从xml转换为JSON服务器端,或者甚至更好地将其上传到服务器转换,这样它就可以从getgo中获取JS。

这样可以最大限度地减少负载,并使文件大小变小。

使用Turboid框架可以实现长循环而不会冻结浏览器。 有了它,你可以编写如下代码:

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

这篇turboid.net文章中的更多细节: Javascript中的实际循环

Javascript是单线程的,所以除了setTimeout ,你可以做的并不多。 如果使用Google Gears是您网站的一个选项,它们可以在真正的后台线程中运行javascript。

你可以使用持续时间为零的setTimeout(),它将根据需要生成

当用户连续刷新页面时,我遇到了同样的问题。 原因是两个嵌套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的主调用者中。

我的意思是这可能不完美,但这已经奏效了。 如果有人仍然认为这可能适合他们,我可以帮助更多真正的代码块。

您可以尝试缩短代码

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

这不会对你造成太大伤害;)

您可以使用HTML5 workers API,但这只适用于Firefox 3.1和Safari 4测试版。

作为@ 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();

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM