Getting started with Async/Await in JavaScript

Published Dec 18, 2017
Getting started with Async/Await in JavaScript

The async/await function is part of the ES2017 specification. The purpose of async/await functions is to simplify the behavior using promises synchronously. You can create an async function by marking a function async:

async function myFn() {
  return 5;
}

When an async function is called, it returns a Promise. If an async function returns a value, the Promise will be resolved with that value:

myFn().then(d => console.log(d)); // -> 5

You can use the await operator inside an async function to "pause" the function until the Promise that is marked with await is resolved:

async function getUserMessages() {
  const userInfo = await userService.getUserInfo(); // function waits here until data comes back
  const userMessages = messageService.getMessagesFor(userInfo.id); // now we can use the user id from the first call to get the messages.
  return userMessages; // userMessages is promise that will resolve with the list of messages.
}

getUserMessages().then(messages => console.log(messages));

It is important to note that by using the async/await mechanism, we are not pausing the JavaScript thread. Rather, the JavaScript engine pauses the execution asynchronously until the result is resolved.

Dawn of Doom

I'm sure you are familiar with a variation of this kind of code containing a lot of nested callbacks:

first(function (d1) {
  second(function (d2) {
    third(function(d3) {
      forth(function (d4) {
        console.log(d1 + d2 + d3 + d4);
      });
    });
  });
});

This happens beause each operation relies on the result of the previous one. Now, if each function returns a promise, you can re-write the above:

function all() {
  var r1;
  var r2;
  var r3;
  first()
  .then(function(d1) {
    r1 = d1;
    return second();
  })
  .then(function(d2) {
    r2 = d2;
    return third();
  })
  .then(function(d3) {
    r3 = d3;
    console.log(r1, r2, r3);
  });
}
all();

Looking at the amount of time that each call takes, you can tell how long it will take in total to see the log result:

  • first(): 1 second
  • second(): 2 seconds
  • third(): 3 seconds
  • total time: 1 + 2 + 3 = 6 seconds

The list above is to emphasize that each promise has to be resolved until we move on to the next one. That's why in total it will take 6 seconds to print the final message to the console.

Enter async/await

Now using async functions and the await operator we can rewrite the all function above:

async function all() {
  const r1 = await first(); // wait until the promise that first() returns is resolved
  const r2 = await second(); // wait until the promise that  second() returns is resolved
  const r3 = await third(); // wait until the promise that third() returns is resolved
  return [r1, r2, r3];
}
all().then(values => console.log(values));

A couple of things to note here:

  • The all function is marked async
  • The await operator pauses the all function until the Promise that its "awaiting on" is resolved
  • If a function is marked async, it will return a Promise. Looking at the all function, we are returning an array of values. Therefore, when we call all, the promise that it returns will be resolved with the array of values.

Error Handling

One of the ways for handling exceptions with async functions is to use a try-catch block:

async function run() {
  try {
    await f1();
    await f2();
    await f3();
  } catch(e) {
    // handle exception
  }
}

There are other ways to handle errors with async/await functions. To learn more about error handling, check out this interesting blog post.


This post is originally published by the author here. This version has been edited for clarity and may appear different from the original post.

Discover and read more posts from Amin Meyghani (AJ)
get started