Get to know MDN better
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.
The await operator is used to wait for a Promise and get its fulfillment value. It can only be used inside an async function or at the top level of a module.
A Promise, a thenable object, or any value to wait for.
The fulfillment value of the promise or thenable object, or, if the expression is not thenable, the expression's own value.
Throws the rejection reason if the promise or thenable object is rejected.
await is usually used to unwrap promises by passing a Promise as the expression. Using await pauses the execution of its surrounding async function until the promise is settled (that is, fulfilled or rejected). When execution resumes, the value of the await expression becomes that of the fulfilled promise.
If the promise is rejected, the await expression throws the rejected value. The function containing the await expression will appear 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.
The expression is resolved in the same way as Promise.resolve(): it's always converted to a native Promise and then awaited. If the expression is a:
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.
Because await is only valid inside async functions and modules, which themselves are asynchronous and return promises, the await expression never blocks the main thread and only defers execution of code that actually depends on the result, i.e., anything after the await expression.
If a Promise is passed to an await expression, it waits for the Promise to be fulfilled and returns the fulfilled value.
Thenable objects are resolved just the same as actual Promise objects.
They can also be rejected:
If the value is not a Promise, await converts the value to a resolved Promise, and waits for it. The awaited value's identity doesn't change as long as it doesn't have a then property that's callable.
If the Promise is rejected, the rejected value is thrown.
You can handle rejected promises without a try block by chaining a catch() handler before awaiting the promise.
This is built on the assumption that promisedFunction() 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:
However, if promisedFunction() does throw an error synchronously, the error won't be caught by the catch() handler. In this case, the try...catch statement is necessary.
You can use the await keyword on its own (outside of an async function) at the top level of a module. This means that modules with child modules that use await 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 the Fetch API and specifying await within the export statement. Any modules that include this will wait for the fetch to resolve before running any code.
When an await 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, another microtask 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:
In this case, the function foo is synchronous in effect, because it doesn't contain any await 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:
However, as soon as there's one await, the function becomes asynchronous, and execution of following statements is deferred to the next tick.
This corresponds to:
The extra then() 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 to foo. 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 use await 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 uses queueMicrotask() to demonstrate how the microtask queue is processed when each await expression is encountered.
In this example, the test() 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 both await and queueMicrotask() 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.
Sometimes, the await is omitted when a promise is directly returned from an async function.
However, consider the case where lastAsyncTask asynchronously throws an error.
Only lastAsyncTask appears in the stack trace, because the promise is rejected after it has already been returned from noAwait — in some sense, the promise is unrelated to noAwait. To improve the stack trace, you can use await 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.
Contrary to some popular belief, return await promise is at least as fast as return promise, due to how the spec and engines optimize the resolution of native promises. There's a proposal to make return promise faster and you can also read about V8's optimization on async functions. Therefore, except for stylistic reasons, return await is almost always preferable.
| ECMAScript® 2027 Language Specification # sec-async-function-definitions |
Enable JavaScript to view this browser compatibility table.
This page was last modified on Jul 8, 2025 by MDN contributors.
Your blueprint for a better internet.
Visit Mozilla Corporation’s not-for-profit parent, the Mozilla Foundation.
Portions of this content are ©1998–2026 by individual mozilla.org contributors. Content available under a Creative Commons license.