async forEach in JavaScript

After writing about Blocking vs Non-Blocking /me was stuck with making forEach of JS asynchronous as it was very much needed for an interesting challenge.

As I was stuck with a broken callback function trying to make async, took help from one of my hacker friends Mortchek to come up with a async loop, as below :

function asyncEach(array, iterator, delay) {
    void function iteration(index) {
        if (index === array.length) return
        iterator(array[index])
        setTimeout(function () { iteration(index + 1) }, delay)
    }(0)
}

Invocation : asyncEach([1, 2, 3], function (v) { console.log(v) }, 1000)

Well, yes there are many libs and others ways of doing this, but this is the best so far, do feel free to modify this for making is even better!

Update 0 : from RadekCZ

function asyncEach(array, fn, delay){
  var i = 0;
  window.setTimeout(function iter(){
    if(i===array.length){
      return;
    }
    fn.call(array, array[i], i++);
    window.setTimeout(iter, delay);
  }, 0);
}

The above function will create only one function for one forEach!

Update 1: from imbcmdth

I have written something similar for my ray tracer for loading and processing models.

Both methods presented so far have a critical failing: those style loops only run at a maximum of 250 iterations per second in modern HTML5 compliant browsers which specifies that the minimum interval between successive timeouts is 4ms regardless of the specified delay.

Performing those loops against a million element array is going to take quite a while!

Another thing missing is that you must have some form of a callback parameter that is called after the asynchronous loop is completed or you can't ever reliably do anything that depends on the results of the loop.

Instead, I propose an asynchronous loop like this:

function asyncEach(array, fn, progress, finished) {
  var i = 0,
      maxBurnTime = 100, // ms to run before yielding to user agent
      finishedFn = finished || progress,
      progressFn = (finishedFn === progress ? null : progress);
 
  function iter() {
    var startTime = Date.now();
 
    while(i < array.length) {
      fn.call(array, array[i], i++);
 
      if(Date.now() - startTime > maxBurnTime) {
        if(progressFn) progressFn(i, array.length);
        return window.setTimeout(iter, 0);
      }
    }
 
    if(progressFn) progressFn(i, array.length);
    if(finishedFn) finishedFn(null, array);
  }
  window.setTimeout(iter, 0);
}

Both callbacks are optional but very useful for large and slow processes or just to maintain a sequence of operations. I use node.js-style callback for finish(error, parameters) but you can use anything you'd like. The point is that by yielding only a few times a second, you can achieve almost native loop performance.

Here is an example use:

var b = 0;
 
//Step 1: Generate 1 million numbers from 0 to 1 billion in steps of 1,000 
//Step 2: Sum the square roots of all 1 million elements in the array
 
asyncEach(
  new Array(1000000),
  function(e, i) {
    this[i] = 1000000000 - i * 1000;
  },
  function(done, total) {
    console.log("Building Array: " + Math.floor( (done/total) * 100) + "%");
  },
  function(err, a) {
    asyncEach(
      a,
      function(e) {
        b += Math.sqrt(e);
      },
      function(done, total) {
        console.log("Summing Square Roots: " + Math.floor( (done/total)* 100 ) + "%");
      },
      function(){
        console.log('Answer: ' + b);
      });
  });

If this wasn't executed asynchronously the user agent would be locked for several seconds.

Share this