我需要遍历一些大型数组并将它们存储在API调用的主干集合中。在不导致循环导致接口(interface)无响应的情况下执行此操作的最佳方法是什么?
由于返回的数据太大,ajax请求的返回也会阻塞。我认为我可以将其拆分并使用setTimeout使其在较小的块中异步运行,但是有一种更简单的方法来执行此操作。
我认为网络 worker 会很好,但需要更改一些保存在UI线程上的数据结构。我尝试使用它来进行ajax调用,但是当它将数据返回到UI线程时,仍然有一段时间接口(interface)不响应。
提前致谢
最佳答案
您可以选择是否使用webWorkers:
没有WebWorkers
对于需要与DOM或应用程序中的许多其他状态进行交互的代码,您不能使用webWorker,因此通常的解决方案是将工作分解为多个块,并在计时器上完成每个块的工作。计时器之间的块之间的中断使浏览器引擎可以处理其他正在进行的事件,不仅可以处理用户输入,还可以绘制屏幕。
通常,您可以负担每个计时器处理多个计时器的效率,并且比每个计时器仅处理一个计时器效率更高,速度更快。此代码使UI线程有机会处理每个块之间的任何未决UI事件,这将使UI保持 Activity 状态。
function processLargeArray(array) {
// set this to whatever number of items you can process at once
var chunk = 100;
var index = 0;
function doChunk() {
var cnt = chunk;
while (cnt-- && index < array.length) {
// process array[index] here
++index;
}
if (index < array.length) {
// set Timeout for async iteration
setTimeout(doChunk, 1);
}
}
doChunk();
}
processLargeArray(veryLargeArray);
这是一个概念的可行示例-不是相同的函数,而是一个不同的长期运行过程,该过程使用相同的
setTimeout()
想法通过多次迭代来测试概率方案:http://jsfiddle.net/jfriend00/9hCVq/您可以将上述内容制作为更通用的版本,以调用类似
.forEach()
这样的回调函数:// last two args are optional
function processLargeArrayAsync(array, fn, chunk, context) {
context = context || window;
chunk = chunk || 100;
var index = 0;
function doChunk() {
var cnt = chunk;
while (cnt-- && index < array.length) {
// callback called with args (value, index, array)
fn.call(context, array[index], index, array);
++index;
}
if (index < array.length) {
// set Timeout for async iteration
setTimeout(doChunk, 1);
}
}
doChunk();
}
processLargeArrayAsync(veryLargeArray, myCallback, 100);
不必一次猜测要分割多少块,还可以让经过的时间作为每个块的指导,并使其在给定的时间间隔内尽可能多地进行处理。不管迭代有多密集的CPU,这都会自动保证浏览器的响应能力。因此,您可以传入毫秒值(或仅使用智能默认值),而不是传入块大小:
// last two args are optional
function processLargeArrayAsync(array, fn, maxTimePerChunk, context) {
context = context || window;
maxTimePerChunk = maxTimePerChunk || 200;
var index = 0;
function now() {
return new Date().getTime();
}
function doChunk() {
var startTime = now();
while (index < array.length && (now() - startTime) <= maxTimePerChunk) {
// callback called with args (value, index, array)
fn.call(context, array[index], index, array);
++index;
}
if (index < array.length) {
// set Timeout for async iteration
setTimeout(doChunk, 1);
}
}
doChunk();
}
processLargeArrayAsync(veryLargeArray, myCallback);
使用WebWorkers
如果循环中的代码不需要访问DOM,则可以将所有耗时的代码放入webWorker中。 webWorker将独立于主浏览器Javascript运行,然后完成后,它可以通过postMessage返回任何结果。
webWorker要求将将在webWorker中运行的所有代码分离到一个单独的脚本文件中,但是它可以完全运行,而不必担心阻塞浏览器中其他事件的处理,也不必担心“无响应脚本”提示当在主线程上执行长时间运行的过程且不阻止UI中的事件处理时,可能会出现这种情况。