await
BaselineWidely available *
This feature is well established and works across many devices and browser versions. It’s been available across browsers since March 2017.
* Some parts of this feature may have varying levels of support.
Theawait
operator is used to wait for aPromise
and get its fulfillment value. It can only be used inside anasync function or at the top level of amodule.
Syntax
await expression
Parameters
expression
A
Promise
, athenable object, or any value to wait for.
Return value
The fulfillment value of the promise or thenable object, or, if the expression is not thenable, the expression's own value.
Exceptions
Throws the rejection reason if the promise or thenable object is rejected.
Description
await
is usually used to unwrap promises by passing aPromise
as theexpression
. Usingawait
pauses the execution of its surroundingasync
function until the promise is settled (that is, fulfilled or rejected). When execution resumes, the value of theawait
expression becomes that of the fulfilled promise.
If the promise is rejected, theawait
expression throws the rejected value. The function containing theawait
expression willappear in the stack trace of the error. Otherwise, if the rejected promise is not awaited or is immediately returned, the caller function will not appear in the stack trace.
Theexpression
is resolved in the same way asPromise.resolve()
: it's always converted to a nativePromise
and then awaited. If theexpression
is a:
- Native
Promise
(which meansexpression
belongs toPromise
or a subclass, andexpression.constructor === Promise
): The promise is directly used and awaited natively, without callingthen()
. - Thenable object (including non-native promises, polyfill, proxy, child class, etc.): A new promise is constructed with the native
Promise()
constructor by calling the object'sthen()
method and passing in a handler that calls theresolve
callback. - Non-thenable value: An already-fulfilled
Promise
is constructed and used.
Even when the used promise is already fulfilled, the async function's execution still pauses until the next tick. In the meantime, the caller of the async function resumes execution.See example below.
Becauseawait
is only valid inside async functions and modules, which themselves are asynchronous and return promises, theawait
expression never blocks the main thread and only defers execution of code that actually depends on the result, i.e., anything after theawait
expression.
Examples
Awaiting a promise to be fulfilled
If aPromise
is passed to anawait
expression, it waits for thePromise
to be fulfilled and returns the fulfilled value.
function resolveAfter2Seconds(x) { return new Promise((resolve) => { setTimeout(() => { resolve(x); }, 2000); });}async function f1() { const x = await resolveAfter2Seconds(10); console.log(x); // 10}f1();
Thenable objects
Thenable objects are resolved just the same as actualPromise
objects.
async function f2() { const thenable = { then(resolve) { resolve("resolved!"); }, }; console.log(await thenable); // "resolved!"}f2();
They can also be rejected:
async function f2() { const thenable = { then(_, reject) { reject(new Error("rejected!")); }, }; await thenable; // Throws Error: rejected!}f2();
Conversion to promise
If the value is not aPromise
,await
converts the value to a resolvedPromise
, and waits for it. The awaited value's identity doesn't change as long as it doesn't have athen
property that's callable.
async function f3() { const y = await 20; console.log(y); // 20 const obj = {}; console.log((await obj) === obj); // true}f3();
Handling rejected promises
If thePromise
is rejected, the rejected value is thrown.
async function f4() { try { const z = await Promise.reject(new Error("rejected!")); } catch (e) { console.error(e); // Error: rejected! }}f4();
You can handle rejected promises without atry
block by chaining acatch()
handler before awaiting the promise.
const response = await promisedFunction().catch((err) => { console.error(err); return "default response";});// response will be "default response" if the promise is rejected
This is built on the assumption thatpromisedFunction()
never synchronously throws an error, but always returns a rejected promise. This is the case for most properly-designed promise-based functions, which usually look like:
function promisedFunction() { // Immediately return a promise to minimize chance of an error being thrown return new Promise((resolve, reject) => { // do something async });}
However, ifpromisedFunction()
does throw an error synchronously, the error won't be caught by thecatch()
handler. In this case, thetry...catch
statement is necessary.
Top level await
You can use theawait
keyword on its own (outside of an async function) at the top level of amodule. This means that modules with child modules that useawait
will wait for the child modules to execute before they themselves run, all while not blocking other child modules from loading.
Here is an example of a module using theFetch API and specifying await within theexport
statement. Any modules that include this will wait for the fetch to resolve before running any code.
// fetch requestconst colors = fetch("../data/colors.json").then((response) => response.json());export default await colors;
Control flow effects of await
When anawait
is encountered in code (either in an async function or in a module), the awaited expression is executed, while all code that depends on the expression's value is paused. Control exits the function and returns to the caller. When the awaited expression's value is resolved, anothermicrotask that continues the paused code gets scheduled. This happens even if the awaited value is an already-resolved promise or not a promise: execution doesn't return to the current function until all other already-scheduled microtasks are processed. For example, consider the following code:
async function foo(name) { console.log(name, "start"); console.log(name, "middle"); console.log(name, "end");}foo("First");foo("Second");// First start// First middle// First end// Second start// Second middle// Second end
In this case, the functionfoo
is synchronous in effect, because it doesn't contain anyawait
expression. The three statements happen in the same tick. Therefore, the two function calls execute all statements in sequence. In promise terms, the function corresponds to:
function foo(name) { return new Promise((resolve) => { console.log(name, "start"); console.log(name, "middle"); console.log(name, "end"); resolve(); });}
However, as soon as there's oneawait
, the function becomes asynchronous, and execution of following statements is deferred to the next tick.
async function foo(name) { console.log(name, "start"); await console.log(name, "middle"); console.log(name, "end");}foo("First");foo("Second");// First start// First middle// Second start// Second middle// First end// Second end
This corresponds to:
function foo(name) { return new Promise((resolve) => { console.log(name, "start"); resolve(console.log(name, "middle")); }).then(() => { console.log(name, "end"); });}
The extrathen()
handler can be merged with the executor passed to the constructor because it's not waiting on any asynchronous operation. However, its existence splits the code into one additional microtask for each call tofoo
. These microtasks are scheduled and executed in an intertwined manner, which can both make your code slower and introduce unnecessary race conditions. Therefore, make sure to useawait
only when necessary (to unwrap promises into their values).
Microtasks are scheduled not only by promise resolution but by other web APIs as well, and they execute with the same priority. This example usesqueueMicrotask()
to demonstrate how the microtask queue is processed when eachawait
expression is encountered.
let i = 0;queueMicrotask(function test() { i++; console.log("microtask", i); if (i < 3) { queueMicrotask(test); }});(async () => { console.log("async function start"); for (let i = 1; i < 3; i++) { await null; console.log("async function resume", i); } await null; console.log("async function end");})();queueMicrotask(() => { console.log("queueMicrotask() after calling async function");});console.log("script sync part end");// Logs:// async function start// script sync part end// microtask 1// async function resume 1// queueMicrotask() after calling async function// microtask 2// async function resume 2// microtask 3// async function end
In this example, thetest()
function is always called before the async function resumes, so the microtasks they each schedule are always executed in an intertwined fashion. On the other hand, because bothawait
andqueueMicrotask()
schedule microtasks, the order of execution is always based on the order of scheduling. This is why the "queueMicrotask() after calling async function" log happens after the async function resumes for the first time.
Improving stack trace
Sometimes, theawait
is omitted when a promise is directly returned from an async function.
async function noAwait() { // Some actions... return /* await */ lastAsyncTask();}
However, consider the case wherelastAsyncTask
asynchronously throws an error.
async function lastAsyncTask() { await null; throw new Error("failed");}async function noAwait() { return lastAsyncTask();}noAwait();// Error: failed// at lastAsyncTask
OnlylastAsyncTask
appears in the stack trace, because the promise is rejected after it has already been returned fromnoAwait
— in some sense, the promise is unrelated tonoAwait
. To improve the stack trace, you can useawait
to unwrap the promise, so that the exception gets thrown into the current function. The exception will then be immediately wrapped into a new rejected promise, but during error creation, the caller will appear in the stack trace.
async function lastAsyncTask() { await null; throw new Error("failed");}async function withAwait() { return await lastAsyncTask();}withAwait();// Error: failed// at lastAsyncTask// at async withAwait
Contrary to some popular belief,return await promise
is at least as fast asreturn promise
, due to how the spec and engines optimize the resolution of native promises. There's a proposal tomakereturn promise
faster and you can also read aboutV8's optimization on async functions. Therefore, except for stylistic reasons,return await
is almost always preferable.
Specifications
Specification |
---|
ECMAScript® 2026 Language Specification # sec-async-function-definitions |