Skip to main content

Documentation Index

Fetch the complete documentation index at: https://docs.syntblaze.com/llms.txt

Use this file to discover all available pages before exploring further.

The await using declaration is a block-scoped variable declaration that implements asynchronous Explicit Resource Management. It guarantees that an object’s cleanup logic is automatically awaited and executed when the execution context exits the block in which the variable is declared, regardless of whether the block exits normally or throws an exception. To utilize await using, the assigned value typically conforms to the AsyncDisposable interface by implementing a method keyed by the well-known symbol Symbol.asyncDispose. However, await using also accepts objects that only implement the synchronous Disposable interface (Symbol.dispose), as well as null or undefined (which safely perform no disposal).

Syntax and Interfaces

Under the hood, TypeScript defines the disposal interfaces as follows:
interface AsyncDisposable {
    [Symbol.asyncDispose](): PromiseLike<void>;
}

interface Disposable {
    [Symbol.dispose](): void;
}
When you declare a variable with await using, TypeScript enforces that the variable is immutable (implicitly const). At the end of the lexical scope, the runtime schedules the [Symbol.asyncDispose]() method to be awaited. If a synchronous Disposable is provided instead, its [Symbol.dispose]() method is executed and its completion is awaited via a resolved Promise.
class AsyncResource implements AsyncDisposable {
    async [Symbol.asyncDispose](): Promise<void> {
        // Asynchronous teardown logic executes here
    }
}

async function execute() {
    await using asyncRes = new AsyncResource();
    await using syncRes = { [Symbol.dispose]: () => {} }; // Valid
    await using emptyRes = null; // Valid, acts as a no-op
    
    // Execution logic
    
} // Implicitly awaits the disposal of all declared resources
Because the disposal phase is asynchronous, await using declarations can only be utilized within async functions, async generators, or modules supporting top-level await.

Execution Order

When multiple await using (or synchronous using) declarations exist within the same scope, their disposal methods are pushed onto a stack. Upon scope exit, they are awaited and executed in reverse order of their declaration (Last-In, First-Out).
async function stackDisposal() {
    await using res1 = new AsyncResource();
    await using res2 = new AsyncResource();
} 
// 1. await res2[Symbol.asyncDispose]()
// 2. await res1[Symbol.asyncDispose]()

Exception Handling and SuppressedError

The await using declaration introduces specific control flow mechanics for error handling. If an exception is thrown during the execution of the block, the disposal methods are still guaranteed to run. If the disposal method also throws an exception while handling the original exception, the runtime prevents the original error from being silently swallowed. Instead, it throws a SuppressedError. Because caught errors in modern TypeScript are of type unknown, you must use a type guard to safely access the properties of the SuppressedError.
class FaultyResource implements AsyncDisposable {
    async [Symbol.asyncDispose]() {
        throw new Error("Disposal failed");
    }
}

async function errorHandling() {
    try {
        await using resource = new FaultyResource();
        throw new Error("Execution failed");
    } catch (err: unknown) {
        if (err instanceof SuppressedError) {
            // err.error holds the Error instance thrown during disposal
            // err.error.message === "Disposal failed"
            
            // err.suppressed holds the original execution Error instance
            // err.suppressed.message === "Execution failed"
        }
    }
}

Compiler Configuration

To use await using in TypeScript, the environment must support the underlying ECMAScript proposal. If targeting older runtimes, you must configure the TypeScript compiler to recognize the global symbols:
// tsconfig.json
{
  "compilerOptions": {
    "target": "ES2022",
    "lib": ["ES2022", "ESNext.Disposable"]
  }
}
In environments where Symbol.asyncDispose, Symbol.dispose, and SuppressedError are not natively implemented, global polyfills must be provided at runtime before the await using declaration is evaluated.
Master TypeScript with Deep Grasping Methodology!Learn More