Async/Await Explained By Doing Your Morning Routine

Video version of this post:

If you have a morning routine before going to work or school, then you can understand async/await in JavaScript.

After you spend a bit of time with promises in JavaScript, you will probably realize that promises can quickly become an unruly mess, just like callbacks!

For example, let’s say that you have 5 different functions that you would like to run in a row, and you try to use promises.

const promiseChain = () => promise1
   .then (promise2)
   .then (promise3)
   .then (promise4)
   .then (promise5)

As long as there are 5 functions named promise1-5, this code will work. However, when you want to go back and read it later, it gives you very little insight as to what is actually going on.

This is where async/await becomes useful. From a functionality perspective, the async/await pattern accomplishes the same thing as a series of promises. But, it is a little easier to read, and gives more guidance on what is actually happening within the async function.

Example of Async/Await in Real World

Let’s use a real-world example to illustrate the use of async/await. Imagine that you have a morning routine composed of 5 activities:

  1. Take a shower
  2. Eat breakfast
  3. Get dressed
  4. Put on makeup
  5. Check the news

Before you can go to work, you need to do all 5 of these activities. But, there are really two ways that you can go through these activities:

  1. Doing each one in a row
  2. Completing all 5 as fast as possible, no matter the order.

Perhaps you (or someone you know) likes to choose the second method 🙂

But, this would probably be disastrous in real life. What if you tried to start getting dressed before you finished showering? How the heck would that work?

So, it is probably best that when we use code to represent this scenario, we make sure that all 5 activities happen in order. We COULD do this with promises, but the async/await pattern may do a better job of showing the logic.

Creating an “Async” Function

You can declare a function with the async keyword. Within an async function, you can then use the await keyword to wait for a promise to be resolved before continuing to the next line of code.

With the combination of these two keywords, you can explicitly set the order.

Please note: this does not change the asynchronous and multi-threaded nature of JavaScript. This only affects the order of promises within a specific async function.

Here an example of an async function:

const morningRoutine = async (startTime) => {

};

We have now declared an async function called morningRoutine that takes one argument called startTime. We will use this in the function to determine if we are going to be late for work or not (but that part comes later),

Okay, now we need to start adding our morning routine. Let’s define a function called shower that takes one parameter. Then, the rest of the promises should wait for that promise to resolve before continuing.

const shower = (time) => new Promise(resolve, reject) => {
  resolve (time);
}

const morningRoutine = async (startTime) => {
  const step1FinishTime = await shower(startTime)
};

Now, we will pass the startTime argument into the shower function. We will not continue to the next statement until that promise is resolved, and a value is returned and stored in step1FinishTime.

Now, let’s pass the value in step1finishTime to the next promise. Here is what that looks like:

const shower = (time) => new Promise(resolve, reject) => {
  resolve (time);
}

const eatBreakfast = (time) => new Promise(resolve, reject) => {
  resolve (time);
}

const morningRoutine = async (startTime) => {
  const step1FinishTime = await shower(startTime)
  const step2FinishTime = await eatbreakfast(step1FinishTime)
};

Here’s what that code looks like in diagram form:

And then we can continue this pattern through all 5 promises like this:

const shower = (time) => new Promise(resolve, reject) => {
  resolve (time);
}

const eatBreakfast = (time) => new Promise(resolve, reject) => {
  resolve (time);
}

const morningRoutine = async (startTime) => {
  const step1FinishTime = await shower(startTime)
  const step2FinishTime = await eatbreakfast(step1FinishTime)
  const step3FinishTime = await getDressed(step2FinishTime)
  const step4FinishTime = await putOnMakeup(step3FinishTime)
  const step5FinishTime = await readNews(step4FinishTime)
  return step5FinishTime;
};

So, the morningRoutine function returns the value stored in step5FinishTime after each promise resolves.

Using “Try” and “Catch” In the Async Function

The example above assumes that each promise actually resolves so that the function can continue.

But what about when one of the promises throws an error? In real-world terms, let’s say you run out of breakfast food, so you take too long in the shower, and you need to text your boss to tell them that you will be late.

You can use “try” and “catch” statements for the entire code block to handle situations where any of the promises fails to resolve.

In this case, our “catch” statement should send a message to our boss.

Here’s what that looks like in code:

const shower = (time) => new Promise(resolve, reject) => {
  resolve (time);
}

const eatBreakfast = (time) => new Promise(resolve, reject) => {
  resolve (time);
}

const morningRoutine = async (startTime) => {
  try {
      const step1FinishTime = await shower(startTime)
      const step2FinishTime = await eatbreakfast(step1FinishTime)
      const step3FinishTime = await getDressed(step2FinishTime)
      const step4FinishTime = await putOnMakeup(step3FinishTime)
      const step5FinishTime = await readNews(step4FinishTime)
      return step5FinishTime;
  }
  catch (e) {
     return e;
  }
};

And here’s what the logic looks like when all promises resolve in diagram form:

And here’s what the logic looks like when one of the promises throws an error in diagram form:

Get More Visual Tutorials

Did you enjoy this tutorial? Sign up here to get more tutorials from CodeAnalogies:

Processing…
Success! You're on the list.

Leave a Reply