2

I have a forloop within a forloop. I'm wondering if I can make the inner forloop run asynchronously instead of blocked code so it'll be faster. Note that this is to be done in vanilla javascript and not using jQuery event handlers.

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;
   }
}
wonghenry
  • 575
  • 2
  • 6
  • 18
  • 1
    `setTimeout` or `requestAnimtationFrame` are your friends. – Felix Kling May 01 '15 at 03:52
  • 5
    It won't run any faster, in fact it'll be several times slower. What is your particular issue? – Qantas 94 Heavy May 01 '15 at 03:52
  • See this question: http://stackoverflow.com/questions/9516900/how-can-i-create-an-asynchronous-function-on-javascript – Sumner Evans May 01 '15 at 03:52
  • @Qantas94Heavy, why is that so? – Sumner Evans May 01 '15 at 03:53
  • @jsve because there is no reason for it to become faster just because it's called later. – zerkms May 01 '15 at 03:54
  • it's faster to just tell the computer to do something rather than tell it to do something later. depending on what the something is... but in this case... async is a lot slower. – Cory Danielson May 01 '15 at 03:54
  • 1
    @jsve: asynchronous code is meant for breaking up operations to allow other things to run while it is waiting for a response, e.g. Ajax or file I/O. Doing this with a plain `for` loop just means that the code has to wait for the next tick to run which slows things down a lot. If the OP actually has a different issue, they should describe what that is. – Qantas 94 Heavy May 01 '15 at 03:55
  • Maybe what you want is webworkers? https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers – Tesseract May 01 '15 at 05:46

3 Answers3

1

In general long loops are not great for running in Javascript, even if they are asynchronous. This is because the threading model is not well specified (this is changing however in the new version being developed) - and consequently there are no guarantees about the semantics of concurrency.

Concurrent == Two things execute at the same instant in time

Asynchronous == One thing finishes before the result from a second thing it started running is available.

It's an important distinction. You can have asynchronous execution in current browsers, but there is no guarantee to have concurrency - so your long loop is very likely going to interfere with and block something else that wants to run, even if it's async. That might be some other setTimeout thread of execution, or a render thread, or an event handler...

So what can you do about this?

A very Javascript-centric thing to do, it's very idiomatic - is to subdivide tasks up into asynchronous units that "continue" from one another. You do a unit of work, and the function that implements it receives the next unit of work, or a closure callback to notify when it is finished. So you might do something like:

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.

Of course, this isn't the fastest way to crank out those work units - as some commenters pointed out. There is overhead when calling functions. But if what you mean by "slowness" is actually lack of responsiveness in the front-end due to those render / event threads blocking, patterns like this will help you.

BadZen
  • 4,083
  • 2
  • 25
  • 48
1

I'm making the following interpretive assumptions about your question:

  • The body of the inner loop in your real code takes some time to execute. count++ is just a stand-in you inserted for the sake of creating an MCVE. (If this assumption is false, then it makes no sense to run the inner loop asynchronously. Incrementing an integer counter is one of the fastest operations you can perform in almost any language.)

  • By "faster" you mean the loop control code here ends faster. Of course the entire task performed by the loops (including the inner loop body) will take much longer if it's done asynchronously. (So again, if this assumption is false, then asynchronicity is inappropriate.)

Given all that, here's how to do it:

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);
}

This schedules the first "loop iteration" to run once all events currently in the event queue are done being handled. Each "iteration" performs its work, then performs the usual loop condition checks, and if needed schedules the next "iteration" to run, again when the event queue has been emptied. The browser is never blocked, and can handle other events (such as user input events), but will fill all remaining time with execution of your "loop".

Community
  • 1
  • 1
radiaph
  • 4,001
  • 1
  • 16
  • 19
1

You can define yourself asynchronous loops but each setTimeout(f, 0) will take some time (usually 1 - 2 ms). For example, this code will take about 1s to finish under windows and nearly 2s under linux.

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");
  });
Tesseract
  • 8,049
  • 2
  • 20
  • 37