For loops are simple, primitive building blocks in code. And they’re everywhere for good reason; we often need to do things repeatedly.
For example, if you fetch data from a database, you likely run the query asynchronously (read: non-blocking — you make the database call, then get notified later when the data is ready). If you want to create a loop that has asynchronous calls in it, things get hairy really fast.
So in this tutorial, let’s take as step back and build up toward truly asynchronous looping!
Let’s start with a basic for loop. This executes 10 times, each time incrementing the variable i and printing it to the console’s output.
Now let’s try to build it asynchronously. That means that if we have other code after this for loop, it’ll run right away and not be stuck waiting on the the loop to finish.
But admittedly, the last snippet is a bit sloppy… It’s both hard to read, and it exposes the variable i (and the function name) to other parts of our code. let’s clean it up…
Here, the variable i is passed into the function so it’s not accessible outside. We can also implicitly call the function right after we define it (saving us a line of code, and reducing the chance of calling the wrong function).
Note that the variable is incremented by just passing in the new value (i+1). Be careful here, though, because i++ would not have worked. Why? Well, i++ increments the variable i after it’s used. That means i++ would just pass the current value of i into the next loop and the loop would never increment (and thus run forever!).
If that last one is tripping you up, it’s probably becuase you’re not used to seeing functions defined and immediately called… here’s a quick example of that:
This creates a function named a and then immediately calls it (which prints out “hello”).
Our async for loop is getting better, but we’re still exposing the function name… and the whole i++ bug is scary (infinite loops are a buzz-kill, for sure)…
And, ideally we’d like the for loop to operate in its own little world where other things can’t poke at it. Have no fear, just wrap it in — you guessed it — another function. :)
Okay. so that may feel awkward at first if you’re not used to asynchronous programming. But, take a few minutes and walk through it. Once you understand what’s going on, the rest is a piece of cake.
We’re just creating a function and immediatelly calling it.. The outer function then acts like a shield that other things can’t get to. Next we initialize the variable i to zero, define the forloop function, and then get things started by calling forloop().
And best of all, now our function names and variables are protected from the outside world, and the way we set i and increment using i++ is a lot more comfortable. win-win!
Okay, so this is a nice programming party trick (seriously, you’ll be a hit at your next party with this one)… But why do we really need asynchronous for loops, anyway?
Here’s an example of getting a file asynchronously in jQuery.
We call $.getJSON and give it a callback function. Then, sometime later it notifies us with the data. Hooray!
But what if you want to loop on asynchronous things, calling one thing after another? For example, you may need to get a reference from one file that tells you what file to get next.
Here’s what that could look like — callbacks within callbacks, all nested together.
Now, 5 levels deep isn’t that bad, but it is definitely harder to read and debug.
And it also breaks a cardinal rule of good programming — don’t repeat code. Repetitive code is bad because if we want to change how this function works later, we will have to make the changes for each function call. That’s tedious and easy to mess up.
But what if we use the asynchronous for loop approach we discussed earlier?
Let’s re-write the nested jQuery call…
Sweet! We just built a sandbox function (known as closure), initialized the variable i, defined the forloop function, and then called it to get things going.
But what about nested for loops? You know, things like this…
No problem. Just lather, rinse, repeat.
ummm. that’s kind of a bummer, man. I mean it’s hard to follow. I’m confused and my brain feels like a pretzel.
ok, ok. yeah it’s a little messy. But if you step through it, you’ll see that it’s just two async. for loops, one inside the other.
But it’s also a little impractical. The whole reason we have the outer function is to protect it from outside influence. The nested for loops are less of a worry, and in most cases not a concern.
So let’s be less strict and let the two for loops share the same sandbox. That’ll flatten things out a level and give us a bit of sanity back. :)
There we go. that’s feeling better, isn’t it?
To recap: all we do in each case is
- make a little sandbox for the for loops (by placing them inside a function).
- initialize the variables (typically i and its friends)
- define each for loop as a function
- double check the logic that jumps between loops.
- get things started by calling the first forloop()
That’s it. You’re ready to make asynchronous for loops like a ninja!
How about one last example to push the limits of this approach a bit.
Let’s make an asynchronous for loop that’s four levels deep, shall we?
Here’s what it looks like normally…
Side note: a four-level deep for loop is generally a horrible idea… but here we go anyway!
Have a better way? Let us know in the comments. We’ve been playing with async web development for a while now but there are always new tricks to learn.
EDIT: Couldn’t stay away from this and had to make a little helper tool. By creating a function (I’ve called mine loop), you can pass in callbacks for each of the steps of the for loop and it takes care of the rest.
And here are some examples to show how you can use it.