Get to know MDN better
This feature is not Baseline because it does not work in some of the most widely-used browsers.
The await using declaration declares block-scoped local variables that are asynchronously disposed. Like const, variables declared with await using must be initialized and cannot be reassigned. The variable's value must be either null, undefined, or an object with a [Symbol.asyncDispose]() or [Symbol.dispose]() method. When the variable goes out of scope, the [Symbol.asyncDispose]() or [Symbol.dispose]() method of the object is called and awaited, to ensure that resources are freed.
The name of the variable to declare. Each must be a legal JavaScript identifier and not a destructuring binding pattern.
valueNInitial value of the variable. It can be any legal expression but its value must be either null, undefined, or an object with a [Symbol.asyncDispose]() or [Symbol.dispose]() method.
This declaration can only be used in places where both await and using can be used, which include:
An await using declares an async disposable resource that's tied to the lifetime of the variable's scope (block, function, module, etc.). When the scope exits, the resource is disposed of asynchronously. Its syntax may be somewhat confusing, because the await does not have an awaiting effect when the variable is first declared, but only when the variable goes out of scope.
When a variable is first declared and its value is non-nullish, a disposer is retrieved from the object. The [Symbol.asyncDispose] property is tried first, and falls back to [Symbol.dispose] if [Symbol.asyncDispose] is undefined. If neither property contains a function, a TypeError is thrown. Notably, the [Symbol.dispose]() method is wrapped into a function that looks like async () => { object[Symbol.dispose](); }, which means if it returns a promise, that promise is not awaited. This disposer is saved to the scope.
When the variable goes out of scope, the disposer is called and awaited. If the scope contains multiple using or await using declarations, all disposers are run in sequence in the reverse order of declaration, regardless of the type of declaration. All disposers are guaranteed to run (much like the finally block in try...catch...finally). All errors thrown during disposal, including the initial error that caused the scope exit (if applicable), are all aggregated inside one SuppressedError, with each earlier exception as the suppressed property and the later exception as the error property. This SuppressedError is thrown after disposal is complete.
The variable is allowed to have value null or undefined, so the resource can be optionally present. As long as one await using variable is declared in this scope, at least one await is guaranteed to happen on scope exit, even if the variable actually has value null or undefined. This prevents the disposal from happening synchronously, causing timing issues (see control flow effects of await).
await using ties resource management to lexical scopes, which is both convenient and sometimes confusing. See below for some examples where it may not behave how you expect. If you want to hand-manage resource disposal, while maintaining the same error handling guarantees, you can use AsyncDisposableStack instead.
You should also check using for more examples, especially some general caveats with respect to scope-based resource management.
Usually, you use await using on some library-provided resource that already implements the async disposable protocol. For example, the Node.js FileHandle is async disposable:
Note that there are two await operations in the declaration for file, which do different things and are both necessary. await fs.open() causes an await during acquisition: it waits for the file to be opened and unwraps the returned promise into a FileHandle object. await using file causes an await during disposal: it makes file disposed asynchronously when the variable goes out of scope.
It's very easy to confuse the following three syntaxes:
It may be even more confusing to know that they can be used together.
First, await y does what you expect: we await the promise y, which is expected to resolve to an object we iterate over. Let us set this variant aside.
The for await...of loop requires the y object to be an async iterable. This means that the object must have a [Symbol.asyncIterator] method that returns an async iterator, whose next() method returns a promise representing the result. This is for when the iterable doesn't know what the next value is, or even if it's done yet, until some async operation is complete.
On the other hand, the await using x syntax requires the x object, as yielded from the iterable, to be an async disposable. This means that the object must have a [Symbol.asyncDispose] method that returns a promise representing the disposal operation. This is a separate concern from the iteration itself, and is only called when the variable x goes out of scope.
In other words, all of the following four combinations are valid and do different things:
Below, we create some fictitious values of y to demonstrate their use cases. For asynchronous APIs, we base our code on the Node.js fs/promises module.
As soon as one await using is declared in a scope, the scope will always have an await on exit, even if the variable is null or undefined. This ensures stable execution order and error handling. The Control flow effects of await examples have more details on this.
In the example below, the example() call below doesn't resolve until one tick after, because of an implicit await when the function returns.
Consider the same code but with a synchronous using instead. This time, the example() call immediately resolves, so the two then() handlers are called in the same tick.
For a more realistic example, consider two concurrent calls to a function:
As you can see, the required 2 resource is disposed in the same tick as required 1. If the optional resource did not cause a redundant await, then required 2 would have been disposed earlier, which would be simultaneous with optional.
| ECMAScript Async Explicit Resource Management # prod-AwaitUsingDeclaration |
Enable JavaScript to view this browser compatibility table.
This page was last modified on Nov 26, 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.