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){

// 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 并让 longFunction 调用自身提供与原始循环方法相当的性能。

我需要另一种解决方案,它具有与循环相当的性能,但不会导致 javascript 执行警告。不幸的是,在这种情况下不能使用 webWorkers。

请务必注意,在此过程中我不需要完全响应式 UI。足以每隔几秒更新一次进度条。

将其分解成循环 block 是一种选择吗? IE。一次执行 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 线程更新。如果不是我们直接再调用它。问题解决了!

最佳答案

其实 1500 次超时不算什么,所以你可以简单地这样做:

var i1 = 0
for (var i = 0; i < 1500; i++) setTimeout(function() { doSomething(i1++) }, 0)

系统会为你排队超时事件,它们会立即一个接一个地被调用。如果用户在执行期间单击任何内容,他们将不会注意到任何延迟。并且没有“脚本运行时间过长”的问题。

根据我的实验,V8 每秒可以创建 500,000 个超时。

更新

如果您需要将 i1 传递给您的 worker 函数,只需传递一个对象,并在您的函数内递增计数器。

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)

在 Node.js 下,您也可以使用 setImmediate(...)

关于javascript - 如何在 javascript 中分解长时间运行的函数,但保持性能,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/16353240/

相关文章:

javascript - Backbone.Paginator 无限模式,带 Marionette

javascript - 鼠标悬停移动图像位置

javascript - 如何在javascript和rails5中获取data-attr值?

javascript - 从 HTML 添加新元素到 div 中

javascript - 即使 JavaScript 验证返回 false,JSP 表单仍在提交

用于移动游戏开发的 Javascript DOM 与 Canvas

javascript - 单击时在 div 中显示图像不起作用

javascript - 无法在 chrome 上滚动容器 div 元素(移动版)

javascript - 为什么 .html() 和 .text() 只选择第一个词?

javascript - 验证不超过一位小数(即 2.2 而非 2.2.3)