The await operator

The await operator

The await operator is one of the most useful things to happen to JavaScript in recent years. But why is it so useful? To find out, let’s take a look at an example.

main.js
index.html
New

    You can probably guess what await does, just by looking at the above example:

    When the await operator is passed a promise, it pauses the function until the promise resolves, and then it returns the promise’s result.

    Await is a lot like promise.then(); they both wait for a promise to resolve and then make the outcome available to subsequently executed code. But unlike promise.then(), the await operator doesn’t require that you create a separate function to handle the outcome. Instead, it just returns it. Neat, huh?

    But putting await aside for the moment, there’s something a little weird about the above example: why is everything wrapped inside a main function?

    Await is asynchronous

    The await operator can’t be used in most JavaScript code. In fact, the only place where await can be used is within async functions. But why this limitation?

    When JavaScript encounters an await operator, it needs to stop and wait until the given promise resolves. But this presents a problem: JavaScript can’t multitask; if it stops execution to wait for a response, the whole browser will stop responding!

    To get around this, the await operator doesn’t really wait for a response. It just stops executing that particular function, and immediately continues on from the point where the function was called. You can confirm this for yourself by comparing the order of the console messages in the following example with their locations in the corresponding code; note how the final line of main() runs after the final line of the script logs “Done!” to the console.

    main.js
    index.html
    New

      Ok, so await pauses the current function without stopping the entire script. But this still doesn’t explain why you’d need a special type of function just for await. After all, wouldn’t the above script make just as much sense without the async keyword?

      But consider this: what would happen if your async function returned a result after the rest of the script had finished executing?

      async function getQuote() {
        // Wait for 100ms to pass to simulate network activity
        await delay(100)
      
        return (
          "Since we decided a few weeks ago to adopt the leaf as legal " +
          "tender, we have, of course, all become immensely rich."
        )
      }
      
      let quote = getQuote()
      
      // This line will be executed before `getQuote()` finishes executing,
      // so `quote` can't contain the returned quote!
      console.log(quote)
      

      This async function’s return statement won’t be reached until after the script’s final console.log() has already been executed. So how could the console.log() access the returned quote? The answer, of course, is via a promise!

      main.js
      index.html
      New

        Because the await operator doesn’t return a result until some point in the future, any functions that use await can only have their result accessed via a promise. And that’s why async functions are the only place where await operators can be used: async functions are guaranteed to return a promise!

        Awaiting non-promises

        The await operator works with any value that you pass to it; it isn’t limited to promises. If you happen to pass something that’s not a promise, it’ll just return the value as is.

        main.js
        index.html
        New

          However, if you do use await with non-promises, keep in mind that await will never return immediately. Even if you await a normal value or an already-resolved promise, it will pause the function and wait until the rest of the script has completed execution – just like promise.then().

          main.js
          index.html
          New

            Parenthesis and precedence

            One special thing about await is that it’s an operator. This means that you can use it without parenthesis.

            main.js
            index.html
            New

              Most code that you’ll see out in the real world doesn’t use parentheses, so I’ll be leaving them out of most of the remaining examples. However, there is one situation in which parenthesis come in handy.

              Await expressions can be used anywhere within your code. They can be used within function calls, within object or array literals, or even as conditions within for or while loops. In fact, you can even dive into an awaited result using . notation. But doing so without the parenthesis will lead to weeping and gnashing of teeth.

              main.js
              index.html
              New

                You’ve probably figured out what’s happening in the above example: the . operator has a higher precedence than the await operator, and so await is being applied to fetchQuote().length. Because fetchQuote() is an async function, it returns a promise, and as promises don’t have a length property, the whole expression reduces to await undefined.

                See if you can fix the above example by adding a set of parenthesis. And once you’ve given it a try, take a look at the solution to see if you’ve added them in the same place as I have.

                When things go wrong

                So to summarize: when you pass the await operator a promise, it waits for the promise to resolve, and then returns the successful outcome. And that’s great and all, but what happens when there’s no successful outcome?

                In the real world, things often go wrong. Promises don’t resolve as expected. And while promise.then() lets you handle this with a rejection handler, await doesn’t know how to deal with failure, so it throws an exception.

                main.js
                index.html
                New

                  Luckily, you can handle exceptions thrown by await the same way that you’d handle any JavaScript exceptions: with try and catch.