I Am
Volodymyr Hudyma
<FrontEndDeveloper />
You Are Here: Home/Async/Await In JavaScript

Async/Await In JavaScript

May 16, 2020

Table Of Contents

    These keywords act as a syntactic sugar built on top of Promises, making asynchronous code look and feel like synchronous, therefore easier to produce and maintain.

    Important note: before proceeding with this article make sure you understand Promises and how to use them. Refer to this article if you don't.

    Async Function

    Async function - it is a function, declared using async keyword. This function always returns a Promise which will be resolved or rejected and knows how to handle await inside of it:

    const example = async () => 1;
    
    console.log(example()); // Prints "Promise { 1 }"

    Note, how the returned value is converted into the Promise. So, how do we actually retrieve the value? (By using then Promise consumer):

    const example = async () => 1;
    
    example()
      .then(result => console.log(result)); // Prints "1"

    Await Keyword

    The real benefits of using async keyword become apparent when you start combining is with await. In fact, await is only valid inside of async functions, you won't be able to use this keyword in a regular function.

    The keyword await makes JavaScript wait until that promise settles(either resolves or rejects) and returns its result:

    const example = async () => {
      const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve("Success");
        }, 1000);
      });
      console.log("Before await");
      const result = await promise;
      console.log("After await");
      return result;
    };
    
    
    /* 
      Prints:
        "Before await"
        Promise { <pending> }
        "After await"
        "Success"
    */
    example()
      .then(result => console.log(result));

    Important note: The function execution “pauses” at the line const result = await promise and resumes when the promise settles, with result becoming its result.

    Let's consider the exact same example, but without using async/await keywords:

    const example = () => {
      const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          resolve("Success");
        }, 1000);
      });
      console.log("Before await");
      const result = promise;
      console.log("After await");
      return result;
    };
    
    
    /* 
      Prints:
        "Before await"
        "After await"
        Promise { <pending> }
        "Success"
    */
    example()
      .then(result => console.log(result));

    Note, how the order of logging "Before await" and "After await" has changed.

    Have you noticed that something is wrong? Why do we still use then when we are talking about async/await?

    Actually, there's a good reason for that: await won’t work in the top-level code, so basically we can't do that (as you remember, await has to be used in pair with async):

    const result = await example(); // Bad

    But it can be wrapped into an anonymous function and executed immediately:

    (async () => {
      const result = await example(); // Good
    })();

    Error Handling

    If the Promise resolves, await promise returns the result, if it rejects - an error is thrown:

    const example = () => {
      const promise = new Promise((resolve, reject) => {
        setTimeout(() => {
          reject("Error");
        }, 1000);
      });
      const result = promise;
      return result;
    };
    
    example()
      .catch(error => console.log(error)); // Prints "Error"

    Basically, both following examples are equivalent:

    const exampleReject = async () => {
      await Promise.reject("Error");
    }
    
    const exampleThrow = async () => {
      throw new Error("Error");
    }

    Thrown error can easily be caught by using try...catch block:

    async function example() {
      try {
        let response = await fetch("http://notexistingresource");
      } catch(error) {
        console.log(error);
      }
    }
    
    example(); // TypeError: Failed to fetch

    The Downsides

    Async/await is really useful to know about, but there are a couple of downsides to consider.

    These keywords make your code look and behave in a synchronous manner.

    The await keyword blocks code execution until the Promise settles, exactly as it would with the synchronous operation.

    Important note: it does allow other tasks to continue, just your own code is blocked. That doesn’t cost any CPU resources, because the engine can do other jobs in the meantime: execute other scripts, handle events, etc.

    In case of having multiple await statements, your code execution is slowed down due to waiting for each promise to resolve.

    Fortunately, this issue can be resolved by storing each Promise in variable and using Promise.all method to wait until all promises complete.

    Try to avoid this:

    const promise1 = new Promise((resolve, reject) => { 
      resolve(1);
    });
    const promise2 = new Promise((resolve, reject) => { 
      resolve(2);
    });
    const promise3 = new Promise((resolve, reject) => { 
      resolve(3);
    });
    
    (async () => {
      await promise1;
      await promise2;
      await promise3;
    })();

    Use Promise.all:

    const promise1 = new Promise((resolve, reject) => { 
      resolve(1);
    });
    const promise2 = new Promise((resolve, reject) => { 
      resolve(2);
    });
    const promise3 = new Promise((resolve, reject) => { 
      resolve(3);
    });
    
    Promise.all([promise1, promise2, promise3])
      .then(result => console.log(result));

    Summary

    Async/await is a very powerful tool and it strongly helps you to increase the readability of your code.

    • Async/await is just syntactic sugar for Promises
    • Async function always returns Promise
    • Await keyword can't be used outside of the async function
    • Await keyword blocks your code execution until the Promise resolves, however other tasks can be done in the meantime
    • Errors can be handled using try...catch block
    • Always remember not to overuse await keyword. Consider using Promise.all instead
    Newsletter
    Receive all new posts directly to your e-mail
    No spam, only quality content twice a week
    Let me know what you think about this article
    Click here to write response...