我有一个长期运行的功能。它遍历一个大数组并在每个循环中执行一个函数。
longFunction : function(){
var self = this;
var data = self.data;
for(var i=0; len = data.length; i<len; i++){
self.smallFunction(i);
}
},
smallFunction : function(index){
// Do Stuff!
}
在大多数情况下,这很好,但是当我处理大约 1500 左右的数组时,我们会收到一条 javascript 执行警报消息。
所以我需要打破这个。我的第一次尝试是这样的:
longFunction : function(index){
var self = this;
var data = self.data;
self.smallFunction(index);
if(data.slides[index+1){
setTimeout(function(){
self.longFunction(index+1);
},0);
}
else {
//WORK FINISHED
}
},
smallFunction : function(index){
// Do Stuff!
}
所以在这里我要删除循环并引入一个自调用函数,每次迭代都会增加它的索引。为了将控制权返回给主 UI 线程以防止 javascript 执行警告方法,我添加了一个setTimeout
允许它在每次迭代后有时间更新的方法。问题是,使用这种方法完成实际工作需要花费 10 倍的时间。看起来正在发生的事情是,虽然setTimeout
设置为 0,但它实际上等待的时间更像是 10 毫秒。它在大型阵列上建立得非常快。删除setTimeout
和 letlongFunction
调用本身可以提供与原始循环方法相当的性能。
我需要另一种解决方案,它具有与循环相当的性能,但不会导致 javascript 执行警告。不幸的是,在这种情况下不能使用 webWorkers。
需要注意的是,在此过程中我不需要完全响应的 UI。足以每隔几秒更新一个进度条。
将它分解成大块循环是一种选择吗?即一次执行 500 次迭代,停止,超时,更新进度条,执行下一个 500 等等.. 等等..
有更好的吗?
回答:
唯一的解决方案似乎是将工作分块。
通过将以下内容添加到我的自调用函数中,我允许 UI 每 250 次迭代更新一次:
longFunction : function(index){
var self = this;
var data = self.data;
self.smallFunction(index);
var nextindex = i+1;
if(data.slides[nextindex){
if(nextindex % 250 === 0){
setTimeout(function(){
self.longFunction(nextindex);
},0);
}
else {
self.longFunction(nextindex);
}
}
else {
//WORK FINISHED
}
},
smallFunction : function(index){
// Do Stuff!
}
我在这里所做的只是检查下一个索引是否可以被 250 整除,如果是,那么我们使用超时来允许主 UI 线程更新。如果没有,我们直接再次调用它。问题解决了!