如何递归调用JQuery .each()迭代

时间:2015-07-03 15:34:04

标签: javascript jquery performance internet-explorer internet-explorer-8

我正在维护不幸在IE8上运行的软件。 IE8的问题在于,如果同步执行太长,它会抛出“脚本无响应”错误:

当Internet Explorer达到一段JavaScript的最大同步指令数时,会显示此消息。 as described here

处理此问题的标准方法类似于

setTimeout(function(){ //slow code }, 1);

但是,在我的情况下,缓慢的部分实际上是:

jQuery(/*selectors*/).each()// iteration

如何审核jQuery().each()找到的元素,其中实际的.each()部分是以超时的方式递归执行的?即使each()块没有任何作用,我仍然会收到弹出警告。有大约20,000个元素可以迭代...我知道......

如果不重写页面上的任何内容,最好的方法是什么(假设我真的无法重写20,000个元素表)。

3 个答案:

答案 0 :(得分:2)

仅供参考,如果您的问题确实发生,因为此操作本身就是:

jQuery(selectors)

需要太长时间,然后您必须将选择器更改为以某种方式评估或更改HTML的速度更快的内容,以便您可以一次查询表的各个部分。 IE8中的jQuery可能会使用Sizzle库来评估选择器,所以如果你有一个大型HTML,选择器和Sizzle的组合对于IE8来说太慢了,那么你将不得不改变其中的一个。 / p>

我们无法在没有看到实际HTML的情况下帮助解决此问题的具体细节,并且可能需要某种测试平台进行试验。我的猜测是,可能有一个更好的选择器,可能使用本机支持的查询机制,如getElementsByTagName()或类似的东西,但我们必须看到实际的HTML,以提出更具体的建议。正如您已经知道,在一个非常慢的浏览器中,20,000个元素只是一个糟糕的配方。

如果您通过选择器查找并且只想获得有关iteartion的帮助,则无法直接使用.each(),因为它将一次运行。相反,您将不得不手动迭代DOM对象的jQuery列表。

function processLargeArray(items) {
    // set check size to whatever number of items you can process at once
    var chunk = 100;
    var index = 0;
    function doChunk() {
        var cnt = chunk;
        while (cnt-- && index < items.length) {

            // process items.eq(index) here

            ++index;
        }
        if (index < items.length) {
            // set Timeout for async iteration
            setTimeout(doChunk, 1);
        }
    }    
    doChunk();    
}

var data = jQuery(selectors);
processLargeArray(data);

仅供参考,此代码适用于jQuery对象,来自关于该主题的更通用的答案:Best way to iterate over an array without blocking the UI

这是一个使用jQuery插件创建类似.each()接口的版本(但它是异步)。

jQuery.fn.eachChunk = function(chunk, eachFn, completeFn) {
    var index = 0;
    var obj = this;
    function next() {
        var temp;
        if (index < obj.length) {
            temp = obj.slice(index, index + chunk);
            temp.each(eachFn);
            index += chunk;
            setTimeout(next, 1);
        } else {
            if (completeFn) {
                completeFn();
            }
        }
    }
    next();
    return this;
};

jQuery(selectors).eachChunk(100, yourFn);

答案 1 :(得分:1)

我非常喜欢Async Library:

https://github.com/caolan/async

为您提供了一系列运行顺序异步函数的选项。

async.each([..], function(callback){
     // Iterator function      
     callback();
});

以与jQuery相同的方式工作,但与jQuery不同,您可以获得更多控制权,例如:

 async.eachSeries([...], function(callback){
   // Will handle each element one at a time
     callback();
 });

async.eachLimit,这意味着您可以控制在任何给定时间运行的任务数量 - 在任何给定时间并行运行x个任务;

所以,例如:

async.eachLimit([...], 2, function(callback){
  // This will run the tasks simultaneously

  callback();
   });

将对数组中的所有元素运行迭代器函数,但会将并发运行的任务数限制为2.如果您需要4个任务,只需将第二个参数更改为4,等等......

所以,例如:

async.eachLimit([...], 2, function(callback){
  // This will run up to 2 tasks simulatenously


  callback();   // If successfull


   });

如果您需要超时以使操作在超时后静默失败(超时的作业不会完成,并且队列的其余部分继续运行。

async.eachLimit([...], 2, function(callback){
  // This will run up to 2 tasks simulatenously


  callback();   // If successfull

  setTimeout(function(){
         return callback();
      }, timeoutLength);


   });

如果您需要超时以使操作无声地失败,(如果出现一个错误,一切都会停止)。

async.eachLimit([...], 2, function(callback){
  // This will run up to 2 tasks simulatenously


  callback();   // If successfull

  setTimeout(function(){
         return callback("Error");
      }, timeoutLength);


   });

我不确定您的工作的具体要求是什么,但我认为异步库是这类内容的理想选择,并且具有很多控制流程灵活性,可以完成您的工作。< / p>

答案 2 :(得分:0)

这是使用each循环的一种jQuery方式:

(function () { //avoid global var for test
    var $elems = $('selector'), // elements to iterate
        chunk = 50; //number of elements before stoping iteration
    (function doStuff() { //recursive method
        $elems.each(function (i) {               
            //delaying method when needed
            if (i && !(i%chunk)) {
                $elems = $elems.slice(i);
                setTimeout(doStuff, 1);
                return false;
            }
            //do slow stuff HERE
            //...
        });
    })();
})();