Writing an Asynchronous 'For-loop' in JavaScript

The other day a friend of mine asked me an interesting question that is sure to be common amongst those new to the world of JavaScript: how do you repeat an asynchronous operation N times?

Traditionally, asynchronous operations in JavaScript are completed when a callback function passed into the initial operation is executed. Thus in order to execute an async operation N times, a callback function must also be executed N times. If N were known before runtime, you could simply nest those callbacks (and end up in callback hell) but that won't save you if N is dynamic.

The answer to this conundrum lies in that which is the bane of AP computer science students everywhere: recursion.

Below is an example of how to serially execute an asynchronous function N times using recursion:

var N = 50

function asyncFunc (cb) {  
  setTimeout(() => cb(Math.random()), 100)
}

function loop (max, results, done) {  
  // Recursion base-case
  if (results.length >= max) return done(results)

  asyncFunc((res) => {
    results.push(res)
    loop(max, results, done)
  })
}

let randomNumbers = []  
loop(N, randomNumbers, function (results) {  
  console.log(results)
})

For ES5 and below, recursion is the only way to do this. With ES6 and the introduction of generators, it's actually possible to use traditional programming control structures such as for and while, but I'll leave that for a different post!