JavaScript Promises and Async/Await with Loops
This article will be an example driven guide on writing promises, consuming them through async/await
code, and using them in loops.
As a note, this article assumes that you are already familiar with concepts such as Blocking vs Non-Blocking code in JavaScript and have maybe even seen (or written) a promise before.
If you are interested in more of a deep dive on promises, be sure to check out Mozilla’s official docs on promises on the MDN website.
The Example We Will Build Up To
For those who want to skip straight to the conclusion, here is the example that we are building up to:
A Classic Way of Delaying Code Execution
A great place to illustrate the benefits of Promises
in JavaScript is to fiddle with a classic built-in function called setTimeout
.
This function takes two parameters (the first being a function that you would like to execute, and the second being time in milliseconds) and delays the execution of the function by that amount of time:
setTimeout(inputFunction, timeInMilliseconds);
It is also common place for programmers to use an anonymous function as the inputFunction
:
The following would output:
1 | print immediately
2 | print after one second
Where line 2
in fact would output into the console after one second.
Now let’s say we wanted to print a third line which prints exactly one second after line 2
. We could go about it in two naive ways:
In one way, we manually change the time value to 2000
in the second invocation of setTimeout
to simulate line 3
executing one second after line 2
:
In another way, we nest the first setTimeout
with another setTimeout
which has the same time value:
Now you could imagine how messy this gets if we wanted to print 100 lines, all one second after another.
Let’s promisify our beloved setTimeout
function and you'll see how printing 100 lines one second after another won't be all that difficult.
Declaring A Promise
A promise is not accessed or created using a reserved word, rather it is an object that must be instantiated using the built-in Promise
class. In the case of promisifying our setTimeout
function, we can declare a new function called delay
like so:
Here our new delay
function returns a Promise
object. The promise takes a function
as its first parameter and captures the two values resolve
and reject
inside of it (which are also functions as we shall explore below).
So far we have kept things simple, but let’s assume that we execute code that can crash or fail, or throw some kind of error in our delayedFunction
, let’s upgrade our example with some basic error handling and add try / catch
blocks. This will let us utilize reject(...)
:
Now, we have a promise based delay
function which we can feed functions into. When we invoke delay
, we will followup by invoking a method made available by our promise object called .then(...)
, lets try it out:
Similar as in our previous example, this also outputs:
1 | print immediately
2 | print me after one second
With the exact timing that the logs imply.
The .then(...)
method runs using the SUCCESSFULLY returned results of the delayedFunction
. We can see them being fed into resolve(...)
. We also have another method called .catch(...)
that we can use to handle rejected promises.
Now this will output:
1 | print immediately
This is a fake error
Here we are intentionally throwing a new Error
with an error message of This is a fake error.
As you can see, we are able to catch it perfectly.
This still doesn’t fully address our problem though. If we wanted to execute a bunch of delay
functions one after the other, we still have to deal with the following:
This becomes really difficult to read as our task(s) grow in complexity (and is also known as CALLBACK HELL). Here is where async / await
comes in.
Async / Await
Long story short, if something returns a Promise
object, you can use async / await
with it and avoid callback hell. Let's transform our previous example to illustrate the benefits.
First we have to declare a top-level async function, what this means is that we are letting JavaScript know that during the execution of a code block, we want to use the await
reserved word inside it.
Now let’s use await
to invoke our delay function in an appropriate fashion:
And we get our expected result of:
1 | print immediately
2 | print after one second
Now let’s add quite a few more lines:
And we get:
What await
is doing is replacing
with
So we can transform the following:
into
Much cleaner isn’t it?
Async / Await in Loops
There may be multiple ways of combining async / await
with iteration, in this article we will explore just two basic ways:
Using a for loop
You can use await
very easily inside of a for ... of
loop:
Using a while loop
You can also use a simple while
loop:
And there you have it. Thanks for reading and happy coding!