Skip to content
Theory · Asynchronous code · lesson 7 of 12

async and await

In a promise chain, each step lives in its own callback, and the code does not read from top to bottom. The words async and await let you write waiting as ordinary lines.

Picture the cook again. A bread recipe has the step "wait for the dough to rise". The cook does not stand over the bowl for an hour: he puts a bookmark in the recipe and cooks other orders. When the dough has risen, he opens the recipe at the bookmark and goes on from the next step. Let's see how this looks in code:

JavaScript
function wait(ms, value) {
    return new Promise((resolve) => {
        setTimeout(() => resolve(value), ms);
    });
}

async function main() {
    const name = await wait(100, "Ann");
    console.log(name);
}

main();
console.log("after main");
// after main
// Ann

Let's take the example apart:

  1. The wait function returns a promise that is fulfilled with value after ms milliseconds.
  2. Inside main, the line with await waits for that and puts "Ann" into the constant name.
  3. The next line, console.log(name), runs only after that and prints Ann.

So the code inside main reads from top to bottom, as if there were no wait. One function may hold many await lines: each of them waits for its own promise in turn.

A function with async

The word async before function makes the function an async function. Such a function always returns a promise, and the value after return becomes that promise's value:

JavaScript
async function one() {
    return 1;
}

const p = one();
console.log(typeof p); // object
console.log(p instanceof Promise); // true
p.then((v) => console.log(v)); // 1

What happens here:

  • one() returns a promise, not the number 1;
  • for a promise, as for an array, typeof gives "object";
  • instanceof Promise tells a promise apart from other objects;
  • the number 1 itself is taken with then or with await.

Interviewers like to ask what a call to an async function returns. The answer: a promise, even if the body says return 1.

What await does

await pauses only its own async function, until the promise on its right is no longer pending. The rest of the program keeps running meanwhile. That is the bookmark in the recipe: the cook set one recipe aside, not the whole kitchen.

That is why after main prints before Ann in the first example. When main reached the await, the call main() returned a promise, and the next line ran.

Even when the promise is already fulfilled, the rest of the function waits, like a then callback, until the current code ends.

Here the cook analogy stops working. A cook may forget where he stopped, but the function resumes exactly at the line after await, with all its variables in place.

Rejection and try…catch

If the promise is fulfilled, await gives its value. If the promise is rejected, await throws its reason, like throw. Such an error is caught with an ordinary try…catch:

JavaScript
async function load() {
    try {
        await Promise.reject(new Error("no network"));
        console.log("loaded");
    } catch (e) {
        console.log(e.message);
    }
}

load();
// no network

Promise.reject(reason) creates a promise that is already rejected. The loaded line is skipped: after the throw, execution moves to catch.

If an error inside an async function is not caught, the function does not crash on the spot. The promise it returned becomes rejected with that error, and it can be caught outside: load().catch((e) => …). Inside the function, try…catch is usually handier: the handling sits next to the line that waits.

That is why try…catch around a call without await does not catch such an error. The call itself throws nothing: it returns a promise at once, and only catch and await see that promise's rejection.

Common mistake

await is written in a function without async:

JavaScript
function show() {
    const name = await wait(100, "Ann"); // ✗
    console.log(name);
}
TEXT
SyntaxError: await is only valid in async functions and the top level bodies of modules

This is a SyntaxError, so the file does not run at all, even if show is never called. The end of the message is about modules, files with import and export. The course does not use them, and in an ordinary file await works only inside an async function. Add async before function.

Key points

  • An async function always returns a promise, and the value after return becomes its value.
  • await pauses only its own function until the promise is no longer pending; the rest of the program goes on.
  • await throws a rejected promise's reason as an error, and try…catch catches it.
  • await works only inside an async function.

Subscribe to take the course

The course is free. Sign in with Telegram and subscribe to the channel to open every lesson.

Sign in another way