Javascript 迭代数组而不阻塞UI的最佳方法
我需要迭代一些大型数组,并将它们存储在API调用的主干集合中。在不使循环导致接口无响应的情况下,最好的方法是什么 由于返回的数据太大,ajax请求的返回也会阻塞。我想我可以将其拆分,并使用setTimeout使其在较小的块中异步运行,但有没有更简单的方法来实现这一点 我认为一个网络工作者会很好,但它需要改变一些保存在UI线程上的数据结构。我已经尝试使用它来进行ajax调用,但是当它将数据返回到UI线程时,仍然会有一段时间接口没有响应Javascript 迭代数组而不阻塞UI的最佳方法,javascript,ajax,arrays,backbone.js,settimeout,Javascript,Ajax,Arrays,Backbone.js,Settimeout,我需要迭代一些大型数组,并将它们存储在API调用的主干集合中。在不使循环导致接口无响应的情况下,最好的方法是什么 由于返回的数据太大,ajax请求的返回也会阻塞。我想我可以将其拆分,并使用setTimeout使其在较小的块中异步运行,但有没有更简单的方法来实现这一点 我认为一个网络工作者会很好,但它需要改变一些保存在UI线程上的数据结构。我已经尝试使用它来进行ajax调用,但是当它将数据返回到UI线程时,仍然会有一段时间接口没有响应 提前感谢您可以选择是否使用webWorkers: 没有网络工作
提前感谢您可以选择是否使用webWorkers: 没有网络工作者 对于需要与DOM或应用程序中的许多其他状态交互的代码,您不能使用webWorker,因此通常的解决方案是将您的工作分解成块,并在计时器上完成每一块工作。使用计时器的块之间的中断允许浏览器引擎处理正在发生的其他事件,不仅允许处理用户输入,还允许绘制屏幕 通常,您可以在每个计时器上处理多个计时器,这比只在每个计时器上处理一个计时器更高效、更快。此代码使UI线程有机会处理每个区块之间的任何挂起的UI事件,从而保持UI的活动状态
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);
与其猜测一次分块的数量,还可以让经过的时间作为每个分块的指南,并让它在给定的时间间隔内处理尽可能多的分块。这在某种程度上自动保证了浏览器的响应性,无论迭代的CPU密集程度如何。因此,您可以传入毫秒值(或使用智能默认值),而不是传入块大小:
//最后两个参数是可选的
函数processLargeArrayAsync(数组、fn、MaxTimePerCunk、上下文){
上下文=上下文| |窗口;
maxTimePerChunk=maxTimePerChunk | | 200;
var指数=0;
函数now(){
返回新日期().getTime();
}
函数doChunk(){
var startTime=now();
虽然(index
function asyncLoop(arr, callback) {
(function loop(i) {
//do stuff here
if (i < arr.Length) { //the condition
setTimeout(function() {loop(++i)}, 1); //rerun when condition is true
} else {
callback(); //callback when the loop ends
}
}(0)); //start with 0
}
asyncLoop(yourArray, function() {
//do after loop
});
//anything down here runs while the loop runs
函数异步循环(arr,回调){
(功能循环(一){
//在这里做事
如果(i
也有类似和的替代方案,其中afaik是,带有前缀。基于@jfriend00,这里是一个原型版本:
if (Array.prototype.forEachAsync == null) {
Array.prototype.forEachAsync = function forEachAsync(fn, thisArg, maxTimePerChunk, callback) {
let that = this;
let args = Array.from(arguments);
let lastArg = args.pop();
if (lastArg instanceof Function) {
callback = lastArg;
lastArg = args.pop();
} else {
callback = function() {};
}
if (Number(lastArg) === lastArg) {
maxTimePerChunk = lastArg;
lastArg = args.pop();
} else {
maxTimePerChunk = 200;
}
if (args.length === 1) {
thisArg = lastArg;
} else {
thisArg = that
}
let index = 0;
function now() {
return new Date().getTime();
}
function doChunk() {
let startTime = now();
while (index < that.length && (now() - startTime) <= maxTimePerChunk) {
// callback called with args (value, index, array)
fn.call(thisArg, that[index], index, that);
++index;
}
if (index < that.length) {
// set Timeout for async iteration
setTimeout(doChunk, 1);
} else {
callback();
}
}
doChunk();
}
}
if(Array.prototype.forEachAsync==null){
Array.prototype.forEachAsync=函数forEachAsync(fn,thisArg,maxTimePerChunk,回调){
让那=这;
设args=Array.from(参数);
设lastArg=args.pop();
if(lastArg instanceof函数){
callback=lastArg;
lastArg=args.pop();
}否则{
回调=函数(){};
}
if(数字(lastArg)==lastArg){
maxTimePerChunk=lastArg;
lastArg=args.pop();
}否则{
maxTimePerChunk=200;
}
如果(args.length==1){
thisArg=lastArg;
}否则{
thisArg=那个
}
设指数=0;
函数now(){
返回新日期().getTime();
}
函数doChunk(){
让startTime=now();
而(index
我已经更新了代码以添加一些功能
使用下面的代码,您可以使用数组函数(迭代数组)或映射函数(迭代映射)
此外,现在还有一个用于在区块完成时调用的函数的参数(如果需要更新加载消息,则会有所帮助),还有一个用于在处理循环结束时调用的函数的参数(异步操作完成后执行下一步所需)
//异步迭代数组
//fn=迭代数组时要调用的函数(对于循环函数调用)
//chunkEndFn(可选,如果不使用,则使用undefined)=块结束时要调用的函数,用于更新加载消息
//endFn(可选,如果不使用,则使用undefined)=在异步执行结束时调用
//最后两个参数是可选的
函数iterateArrayAsync(数组、fn、chunkEndFn、endFn、MaxTimePerCunk、上下文){
上下文=上下文| |窗口;
maxTimePerChunk=maxTimePerChunk | | 200;
var指数=0;
函数now(){
返回新日期().getTime();
}
函数doChunk(){
var startTime=now();
而(索引function asyncLoop(arr, callback) {
(function loop(i) {
//do stuff here
if (i < arr.Length) { //the condition
setTimeout(function() {loop(++i)}, 1); //rerun when condition is true
} else {
callback(); //callback when the loop ends
}
}(0)); //start with 0
}
asyncLoop(yourArray, function() {
//do after loop
});
//anything down here runs while the loop runs
if (Array.prototype.forEachAsync == null) {
Array.prototype.forEachAsync = function forEachAsync(fn, thisArg, maxTimePerChunk, callback) {
let that = this;
let args = Array.from(arguments);
let lastArg = args.pop();
if (lastArg instanceof Function) {
callback = lastArg;
lastArg = args.pop();
} else {
callback = function() {};
}
if (Number(lastArg) === lastArg) {
maxTimePerChunk = lastArg;
lastArg = args.pop();
} else {
maxTimePerChunk = 200;
}
if (args.length === 1) {
thisArg = lastArg;
} else {
thisArg = that
}
let index = 0;
function now() {
return new Date().getTime();
}
function doChunk() {
let startTime = now();
while (index < that.length && (now() - startTime) <= maxTimePerChunk) {
// callback called with args (value, index, array)
fn.call(thisArg, that[index], index, that);
++index;
}
if (index < that.length) {
// set Timeout for async iteration
setTimeout(doChunk, 1);
} else {
callback();
}
}
doChunk();
}
}
//Iterate Array Asynchronously
//fn = the function to call while iterating over the array (for loop function call)
//chunkEndFn (optional, use undefined if not using) = the function to call when the chunk ends, used to update a loading message
//endFn (optional, use undefined if not using) = called at the end of the async execution
//last two args are optional
function iterateArrayAsync(array, fn, chunkEndFn, endFn, 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((now() - startTime) > maxTimePerChunk && chunkEndFn !== undefined){
//callback called with args (index, length)
chunkEndFn.call(context,index,array.length);
}
if (index < array.length) {
// set Timeout for async iteration
setTimeout(doChunk, 1);
}
else if(endFn !== undefined){
endFn.call(context);
}
}
doChunk();
}
//Usage
iterateArrayAsync(ourArray,function(value, index, array){
//runs each iteration of the loop
},
function(index,length){
//runs after every chunk completes, this is optional, use undefined if not using this
},
function(){
//runs after completing the loop, this is optional, use undefined if not using this
});
//Iterate Map Asynchronously
//fn = the function to call while iterating over the map (for loop function call)
//chunkEndFn (optional, use undefined if not using) = the function to call when the chunk ends, used to update a loading message
//endFn (optional, use undefined if not using) = called at the end of the async execution
//last two args are optional
function iterateMapAsync(map, fn, chunkEndFn, endFn, maxTimePerChunk, context) {
var array = Array.from(map.keys());
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, key, map)
fn.call(context,map.get(array[index]), array[index], map);
++index;
}
if((now() - startTime) > maxTimePerChunk && chunkEndFn !== undefined){
//callback called with args (index, length)
chunkEndFn.call(context,index,array.length);
}
if (index < array.length) {
// set Timeout for async iteration
setTimeout(doChunk, 1);
}
else if(endFn !== undefined){
endFn.call(context);
}
}
doChunk();
}
//Usage
iterateMapAsync(ourMap,function(value, key, map){
//runs each iteration of the loop
},
function(index,length){
//runs after every chunk completes, this is optional, use undefined if not using this
},
function(){
//runs after completing the loop, this is optional, use undefined if not using this
});