如何在javascript中异步运行forloop中的函数

时间:2015-05-01 03:49:46

标签: javascript

我在forloop中有一个forloop。我想知道我是否可以异步运行内部forloop而不是阻塞代码,因此它会更快。请注意,这是在vanilla javascript中完成的,而不是使用jQuery事件处理程序。

var count = 0;
for(var i = 0; i < 1000000; i++){
   //HELP: run this forloop asynchonously
   for(var j = 0; j < 1000000; j++){
      count++;// doesn't matter when when we do the +1;
   }
}

3 个答案:

答案 0 :(得分:1)

通常,长循环不适合在Javascript中运行,即使它们是异步的。这是因为没有很好地指定线程模型(这在正在开发的新版本中正在改变) - 因此不能保证并发的语义。

Concurrent ==两件事在同一时刻执行

Asynchronous ==在开始运行的第二件事的结果出现之前,有一件事情已经完成。

这是一个重要的区别。您可以在当前浏览器中进行异步执行,但不能保证具有并发性 - 因此您的长循环很可能会干扰并阻止其他想要运行的东西,即使它是异步的。这可能是其他一些setTimeout执行线程,或渲染线程,或事件处理程序......

那么你能做些什么呢?

一个非常以Javascript为中心的事情,它是非常惯用的 - 是将任务细分为异步单元,并继续&#34;彼此你做一个工作单元,实现它的函数接收下一个工作单元,或者一个闭包回调函数在完成时通知。所以你可以这样做:

function loopProcessing(cb) {
     var index = 0;
     worker = function(cb) {
        index += 1;
        // Process expensive work unit /index/
        if (index >= MAX_INDEX) setTimeout(cb,0);
        setTimeout(worker,0,cb);
     }
     setTimeout(worker,0,cb);
}

loopProcessing(function() { alert("Finished all work!"); });
// This call returns immediately, and other threads of execution 
//  in the system will never have to wait more than one work unit.

当然,正如一些评论者指出的那样,这并不是制定这些工作单位的最快方式。调用函数时会有开销。但如果你的意思是&#34;慢慢&#34;实际上由于那些渲染/事件线程阻塞,前端缺乏响应能力,这样的模式会对你有所帮助。

答案 1 :(得分:1)

我对您的问题做出以下解释性假设:

  • 真实代码中内部循环的主体需要一些时间来执行。 count++只是为了创建MCVE而插入的替代。 (如果这个假设是假的,那么异步运行内部循环是没有意义的。增加整数计数器是几乎任何语言都可以执行的最快的操作之一。)

  • 通过&#34;更快&#34;你的意思是这里的循环控制代码结束得更快。当然,如果它以异步方式完成,那么循环(包括内循环体)执行的整个任务将花费更长的时间。 (再次,如果这个假设是错误的,那么异步性是不合适的。)

鉴于这一切,这里有如何做到这一点:

var count = 0;
var i = 0, j = 0;
setTimeout(innerLoopBody, 0);

function innerLoopBody(){
   count++; // or whatever you want the inner loop body to do
   j++;
   if (j >= 1000000){
      j = 0;
      i++;
      if (i >= 1000000) return;
   }
   setTimeout(innerLoopBody, 0);
}

这将安排第一次&#34;循环迭代&#34;在处理完事件队列中当前所有事件后运行。每个&#34;迭代&#34;执行其工作,然后执行通常的循环条件检查,并在需要时安排下一个&#34;迭代&#34;在事件队列清空后再次运行。浏览器永远不会被阻止,并且可以处理其他事件(例如用户输入事件),但会通过执行&#34;循环来填充所有剩余时间。

答案 2 :(得分:1)

您可以定义自己的异步循环,但每个setTimeout(f, 0)需要一些时间(通常为1 - 2 ms)。 例如,这个代码在Windows下完成大约需要1秒,在linux下大约需要2秒。

function whileAsync(cond, body, cb) {
  setTimeout(function() {
    if(cond()) {
      body();
      whileAsync(cond, body, cb);
    } else cb();
  }, 0);
}

function forRangeAsync(start, end, body, cb) {
  whileAsync(function() {
      return start < end;
    },
    function() {
      body(start++);
    }, cb);
}

var start = Date.now();

forRangeAsync(0, 1000, function(i) {
    console.log(i);
  }, function() {
    var end = Date.now();
    console.log("finished");
    console.log("time = " + (end - start) + "ms");
  });