JavaScript Promises and Async/Await with Loops

Nafeu Nasir
4 min readJan 18, 2021

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!

--

--

Nafeu Nasir

is a musician and full-stack web developer from Toronto, Canada who studied Computer Science and Linguistics at the University of Toronto.