如何递归调用 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);
});
我不确定您的工作的确切要求是什么,但我认为异步库是这类东西的理想选择,并且具有很大的控制流灵活性来完成您的工作。
我正在维护的软件很遗憾必须在 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);
});
我不确定您的工作的确切要求是什么,但我认为异步库是这类东西的理想选择,并且具有很大的控制流灵活性来完成您的工作。