Skip to main content

Async / Await

Async function

The word "async" before a function means one thing:

a function always return a promise.

Other values are wrapped in a resolved promise automatically.

async function f() {
return 1
}

f().then(alert) // 1

We could explicitly return a promise, which would be the same:

async function f() {
return Promise.resolve(1)
}

f().then(alert) // 1

async ensures that the function returns a promise, and wraps non-promises in it.

Await

await works only inside async functions.

let value = await promise;

The keyword await makes JavaScript wait until that promise settles and returns its result.

await literally suspends the function execution until the promise settles, and then resumes it with the promise result.

That doesn't cost any CPU resources, because the JavaScript engine can do other jobs in the meantime.

Modern browsers allow top-level await in modules

In modern browsers, await on top level works just fine, when we're inside a module.

// we assume this code runs at top level, inside a module.
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();

console.log(user);

If we're not using modules, or older browsers must be supported, there's a universal recipe: wrapping into an anonymous async function.

(async () => {
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
...
})();

Async class methods

To declare an async class method, just prepend it with async:

class Waiter {
async wait() {
return await Promise.resolve(1);
}
}

new Waiter()
.wait()
.then(alert); // 1 (this is the same as (result => alert(result)))

The meaning is the same: it ensures that the returned value is a promise and enables await.

Error handling

If a promise resolves normally, then await promise returns the result.

But in the case of a rejection, it throws the error, just as if there were a throw statement at that line.

This code

async function f() {
await Promise.reject(new Error("Whoops!"))
}

is the same as this

async function f() {
throw new Error("Whoops!")
}

In real situations, the promise may take some time before it rejects. In that case there will be a delay before await throws an error.

We can catch that error using try ... catch, the same way as a regular throw:

async function f() {
// we can wrap multiple lines
try {
let response = await fetch('http://no-such-url')
let user = await response.json()
} catch(err) {
// catches errors both in fetch and response.json
alert(err)
}
}