如何在javascript中分解长时间运行的函数,同时保持性能

如何在javascript中分解长时间运行的函数,同时保持性能,javascript,Javascript,我有一个长期运行的功能。它遍历一个大数组并在每个循环中执行一个函数 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)

我有一个长期运行的功能。它遍历一个大数组并在每个循环中执行一个函数

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!

}
因此,我在这里删除循环,并引入一个自调用函数,它会在每次迭代中增加索引。为了将控制权返回到主UI线程以防止javascript执行警告方法,我添加了一个
setTimeout
,以允许它在每次迭代后有时间进行更新。问题是,用这种方法完成实际工作实际上需要10倍的时间。虽然
setTimeout
设置为0,但实际情况似乎是等待10毫秒。它在大型阵列上构建得非常快。删除
setTimeout
并让
longFunction
调用本身可以提供与原始循环方法相当的性能

我需要另一个解决方案,它的性能与循环相当,但不会导致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线程更新。如果不是,我们直接再打一次。问题解决了

下面是一些批处理代码,这些代码是根据我之前的回答修改的:

var n = 0,
    max = data.length;
    batch = 100;

(function nextBatch() {
    for (var i = 0; i < batch && n < max; ++i, ++n) {
        myFunc(n);
    }
    if (n < max) {
        setTimeout(nextBatch, 0);
    }
})();
var n=0,
max=数据长度;
批次=100;
(函数nextBatch(){
对于(变量i=0;i
实际上1500次超时算不了什么,所以您可以简单地执行以下操作:

var i1 = 0
for (var i = 0; i < 1500; i++) setTimeout(function() { doSomething(i1++) }, 0)
在Node.js下,您还可以使用
setImmediate(…)

还有更好的吗

如果你同意它只在现代浏览器中工作,那么你应该研究一下“网络工作者”,它可以让你在后台执行JS


是否可以在后台异步运行它?如果你不能做到这一点,是的,把它分成几块。你的函数对每一项具体做了什么?也许您可以通过一次执行多个setTimeout来并行它们。@Patashu-web workers在所有浏览器中都不可用。。。所以分成合理的块看起来是安全的方法。
setTimeout
的最小值为4ms。是的,将循环分解为500个循环是一个不错的方法。@gordyr-iFrame确实占用了相当大的开销,似乎要花12秒才能生成1000个循环。您的方法和我刚才添加到问题中的方法都非常有效。我希望你的速度稍微快一点,因为这样可以避免函数开销。谢谢alnitak。虽然这是正确的,但它会大大降低速度,因为我们对每个迭代都使用超时。在大多数浏览器中,0超时实际上等于10毫秒左右,这意味着对于一组2000长的进程,我们将在不需要的加载时间上增加20秒。我们提出的解决方案只在指定的时间间隔内运行超时,允许其他解决方案立即启动。我确信您不理解此解决方案。设置超时将同时启动。在第一个开始执行之前,将经过几毫秒。但其他人将毫不拖延地跟进。当我说1500次超时不算什么时,我并不是说1500*10ms不算多。我的意思是V8可以轻松地容纳数千个等待的超时,他们的时间已经到了!所以他们会一个接一个的执行。哦,我明白了!!对不起,我完全误入歧途了。嗯,这实际上可能是最好的解决方案。我会做一些测试,然后再给你回复。如果它比我现在使用的更好,我会告诉你答案。谢谢是的,在测试了你的建议之后,它是完美的,除了不能保证顺序这一事实。我将它们封装在一个匿名函数中,并传入索引,但在大型数组中,我发现了顺序上的变化。可能是其中一个超时在另一个超时之前执行。我不知道为什么会这样。我本以为所有浏览器都会对它们进行排队,但事实似乎并非如此。如果我在某个地方犯了错误,我会做一些进一步的测试。我对webworkers有很多经验,但不幸的是,由于DOM访问限制,它们不适用于这种情况,因此无法使用。
var i1 = 0
for (var i = 0; i < 1500; i++) setTimeout(function() { doSomething(i1++) }, 0)
function doSomething(obj) {
   obj.count++
   ...put actual code here
}
var obj = {count: 0}
for (var i = 0; i < 1500; i++) setTimeout(function() { doSomething(obj) }, 0)