0 day streak
typescript / expert
Snippet

Deterministic Resource Cleanup with `await using`

`await using` (TC39 Explicit Resource Management, supported since TypeScript 5.2) binds an async-disposable to a block scope and invokes its `Symbol.asyncDispose` method when the scope exits — even when an exception escapes. This eliminates the try/finally boilerplate that traditionally protected connection pools, file handles and locks, and guarantees ordered teardown across multiple resources in LIFO order. Combined with `AsyncDisposable`, you get RAII semantics that survive async control flow.

snippet.ts
typescript
1
2
3
4
5
6
7
8
9
10
11
12
class DbConnection implements AsyncDisposable {
constructor(public readonly id: string) {}
async query(sql: string): Promise<unknown[]> { return []; }
async [Symbol.asyncDispose](): Promise<void> {
console.log(`closing ${this.id}`);
}
}
async function fetchUsers(): Promise<unknown[]> {
await using conn = new DbConnection("main");
return conn.query("SELECT * FROM users");
}
Breakdown
1
class DbConnection implements AsyncDisposable {
Declares conformance to the AsyncDisposable contract.
2
async [Symbol.asyncDispose](): Promise<void> {
Cleanup hook the runtime awaits when the binding leaves scope.
3
await using conn = new DbConnection("main");
Binds conn for the rest of the block; disposal runs even on throw.
4
return conn.query("SELECT * FROM users");
The pending promise is awaited before disposal because of await using.