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

How to call JQuery .each() iteration recursively

我正在维护的软件很遗憾必须在 IE8 上 运行。 IE8 的问题在于,如果同步执行时间过长,它会抛出 'script unresponsive' 错误:

当 Internet Explorer 达到 JavaScript. as described here

的最大同步指令数时显示此消息

处理这个问题的标准方法是

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

但是,就我而言,缓慢的部分实际上是:

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

如何遍历使用 jQuery().each() 找到的元素,其中实际的 .each() 部分是通过超时递归执行的?即使 each() 块什么都不做,我仍然会收到弹出警告。大约有 20,000 个元素要遍历...我知道...

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

仅供参考,如果您的问题实际发生是因为这个操作本身:

jQuery(selectors)

花费的时间太长,那么您将不得不将选择器更改为可以更快地评估或更改 HTML 的东西,以便您可以在某个位置查询 table 的片段时间。 IE8 中的 jQuery 可能会使用 Sizzle 库来评估选择器,因此如果您有大型 HTML、选择器和 Sizzle 的组合,它们对于 IE8 来说太慢了,那么您将不得不更改三者之一。

如果没有看到实际的 HTML 并且可能没有某种测试台可以进行试验,我们无法帮助解决这个问题的具体问题。我的猜测是可能有更好的选择器,也许使用本地支持的查询机制,例如 getElementsByTagName() 或类似的东西,但我们必须看到实际的 HTML 才能提出更具体的建议.如您所知,在一个非常慢的浏览器中放置 20,000 个元素是一个糟糕的开始。


如果您通过选择器查找并且只是想获得迭代方面的帮助,您不能直接使用 .each(),因为它会同时 运行。相反,您将不得不手动迭代 jQuery 对象的 DOM 列表。

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

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

(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
            //...
        });
    })();
})();

我非常喜欢异步库:

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

迭代器会运行遍历数组中的所有元素,但会将并发运行ning任务的数量限制为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);


   });

我不确定您的工作的确切要求是什么,但我认为异步库是这类东西的理想选择,并且具有很大的控制流灵活性来完成您的工作。