A Beginner's Guide to Async/Await

joannat

Joanna

Posted on December 15, 2019

A Beginner's Guide to Async/Await

Async/await was rolled out with ECMAScript 2017, and enables our asynchronous code to flow even more like synchronous code. It's basically syntactic sugar for Promises, so like Promises, it helps us avoid writing deep chains of nested callbacks, and is arguably even more readable than regular Promises.

Syntax

To declare an async function, put async at the beginning of your function declaration.

Like so:

async function greeting(word) {
    return word;
}
Enter fullscreen mode Exit fullscreen mode

Using the async keyword lets the interpreter know that the function should evaluate to a Promise instead of directly returning a value.

So the function above is effectively the same as:

function greeting(word) {
   return Promise.resolve(word);
}
Enter fullscreen mode Exit fullscreen mode

In the second function, we are explicitly stating that this function returns a Promise. In the first one, the async keyword is doing that implicitly for us.

Let's resolve it

Technically, we can call an async function just like a normal Promise, using a .then() statement to make sure it evaluates to a value, and not just a Promise object.

async function greeting(word) {
    return word;
}

greeting('Hi'); // Promise { ‘Hi’ } <-- evaluates to a Promise object

greeting('Hey')
.then(word => console.log(word)); // ‘Hey’ <-- evaluates to a fulfilled Promise
Enter fullscreen mode Exit fullscreen mode

If we call greeting by itself, then it’s going to evaluate to a pending Promise object, so we could use .then() to make sure it the Promise is fulfilled.

But (a)wait!

What makes an async function so exciting is the possibility of using the await keyword. When we put await in front of an expression in our async function, we’re telling the interpreter, hey, this expression returns a Promise, so wait-- pause code execution here until the function that follows await is finished running, and then resolve that Promise!

Let's try it out

async function makeCall() {
   try {
      const response = await axios.get('https://somewebsite.com');
      console.log(response.data);
   } catch(error) {
      console.log(error);
   }
};
Enter fullscreen mode Exit fullscreen mode

So we’ve got an async function makeCall(). Our axios GET request is being called with the await keyword, which means that nothing happens with the rest of makeCall() until the response is received from the axios request.

The axios library is also built on Promises-- axios requests return Promises. But if a function does not inherently return a Promise, await turns it into one that does.

The await keyword basically injects the resolve of a Promise that would otherwise need to be finished/fulfilled with a .then().

Here, we’re storing that resolve in a variable because we want to do something with it-- namely, grab the data from that response object and then console log it.

(We handle our errors by placing the body of our main call in a try block, and follow that up with a catch block.)

Pretty cool! And a bit easier to read than this:

function makeCall() {
    return axios.get('https://somewebsite.com')
        .then((response) => {
        console.log(response)
    }).catch((error) => {
        console.log(error)
    });
}
Enter fullscreen mode Exit fullscreen mode

Avoid chaining

With normal Promises, when we have multiple Promises that need to be resolved, our code could look something like this:

function makeThreeCalls() {
    return axios.get('https://someurl.com')
    .then(() => {
        axios.get('https://anotherurl.com')
    }).then(() => {
        axios.get('https://yetanotherurl.com')
    }).catch((error) => {
        console.log(error);
    })
}
Enter fullscreen mode Exit fullscreen mode

This is definitely better than callback hell. But we're still left with this chain of .then()s, and each of those .then()s have their own arrow function.

Using async/await makes our code even more readable, especially when multiple calls are involved.

async function makeThreeCalls() {
    const firstCall = await axios.get('https://someurl.com');
    const secondCall = await axios.get('https://anotherurl.com');
    const thirdCall = await axios.get('https://yetanotherurl.com');
    console.log(`${firstCall}, ${secondCall}, ${thirdCall}`)
}
Enter fullscreen mode Exit fullscreen mode

This is an async function with three await statements in one block of code. It just reads a little more like synchronous code, doesn't it?

We've got three axios requests that are preceded by the await keyword, which means they’re going to be resolved Promises. Our interpreter waits for one request to be done before it invokes the next.

Promise.all()

We can speed this process up the same way we would with normal Promises. Instead of running the functions one by one, waiting for one function to resolve before we call the next, we can run them in parallel. We do this by calling them with Promise.all().

async function makeThreeCalls() {
    const [ firstCall, secondCall, thirdCall ] = Promise.all([
        await axios.get('https://someurl.com'), 
        await axios.get('https://anotherurl.com'),
        await axios.get('https://yetanotherurl.com')
    ]);
    console.log(`${firstCall}, ${secondCall}, ${thirdCall}`)
}
Enter fullscreen mode Exit fullscreen mode

This saves a lot of time.

~~~~

I hope this was a helpful introduction to async/await, which is just syntactic sugar on top of Promises. It can make your code even more intuitive and easier to read. It allows us to write asynchronous code that reads more like synchronous code, and helps us avoid the .then() chains that are still necessary with normal Promises.

💖 💪 🙅 🚩
joannat
Joanna

Posted on December 15, 2019

Join Our Newsletter. No Spam, Only the good stuff.

Sign up to receive the latest update from our blog.

Related