【问题标题】:How to stop intense Javascript loop from freezing the browser如何阻止激烈的 Javascript 循环冻结浏览器
【发布时间】:2010-10-17 10:16:52
【问题描述】:

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

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

【问题讨论】:

  • 获得更快的语言!不,真的:除非绝对必要,否则不要为此使用 JS - 如您所见,它是 1) 单线程和 2) 慢。
  • 这是客户端功能,需要JS。
  • @Triptych - 他的选择是什么?当然有人会希望像这样的繁重工作可以在服务器端执行,但由于我们不知道他的情况,最好假设他有充分的理由在客户端执行此操作,并且在 Web 应用程序中工作客户端时,您只能在 Javascript 和... Javascript 之间进行选择。
  • JavaScript 中重计算的最佳实践stackoverflow.com/q/13947592/821057

标签: javascript jquery performance loops


【解决方案1】:

我会放弃“每个”函数,转而使用 for 循环,因为它更快。我还会使用“setTimeout”添加一些等待,但仅在需要时才经常使用。您不想每次等待 5 毫秒,因为处理 3500 条记录大约需要 17.5 秒。

下面是一个使用 for 循环的示例,该循环以 5 毫秒的间隔处理 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");

希望这会有所帮助。

【讨论】:

  • 这是个好主意 - 定期使用 setTimeout。它的超时时间为 0。
  • 我已经为几个需要在客户端处理大量数据的 Web 应用程序做到了这一点。工作起来就像一个魅力,即使它确实需要一些重组。
  • 很酷的代码。也许我遗漏了一些东西,但我必须在 setTimeout() 之后添加 index++break 才能使其正常工作。
  • 我不知道我是否遗漏了什么,但是根据您在此处提供的内容进行成型时,我的代码将进入无限循环。
  • 更好地使用 console.time() 和 console.timeEnd() 进行基准测试
【解决方案2】:

在处理之间设置一个超时时间,以防止循环周期耗尽所有浏览器资源。总的来说,处理和循环所有内容只需要几秒钟,对于 3,500 个元素来说并非不合理。

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

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

  //process element;

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

processing();

【讨论】:

  • 我决定采用这种方法,但我只每 50 个元素运行一次 setTimeout。是的,它的超时时间为 0。
  • @Christoph - 您不需要传递任何超时间隔“0”,因为默认值为 0
【解决方案3】:

我会考虑将 3500 个元素从 xml 转换为 JSON 服务器端,甚至更好地将其上传到转换后的服务器,以便它从一开始就为 JS 原生。

这将最大限度地减少您的负载,并使文件大小也更小。

【讨论】:

    【解决方案4】:

    你可以 setTimeout() 的持续时间为零,它会根据需要产生

    【讨论】:

      【解决方案5】:

      Turboid 框架可以在不冻结浏览器的情况下进行长时间循环。有了它,你可以编写如下代码:

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

      此 turboid.net 文章中的更多详细信息:Real loops in Javascript

      【讨论】:

        【解决方案6】:

        Javascript 是单线程的,所以除了setTimeout,你无能为力。如果您的网站可以选择使用 Google Gears,它们提供了在真正的后台线程中运行 javascript 的能力。

        【讨论】:

          【解决方案7】:

          您可以使用 HTML5 workers API,但这仅适用于 Firefox 3.1 和 Safari 4 betas atm。

          【讨论】:

          • 我同意 Workers API。
          • 但是你不能从 Worker API 进行 DOM 操作
          【解决方案8】:

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

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

          【讨论】:

            【解决方案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();
              

              【讨论】:

                猜你喜欢
                • 2017-05-20
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2018-03-08
                • 2019-11-09
                • 1970-01-01
                • 1970-01-01
                相关资源
                最近更新 更多