Forgot db.close()?
Not anymore.
Handling resources in JavaScript has always been risky. try/finally blocks are verbose and easy to mess up.
If you forget to close a database connection or file handle, your server eventually crashes.
02.
Enter using
Old Way (Try/Finally)
const conn = await db.connect();
try {
await conn.query(...);
} finally {
await conn.close(); // ๐ซ Easy to forget
}
New Way (Scope-Based)
{
using conn = await db.connect();
await conn.query(...);
}
// โ
Automatically disposed when block exits!
03. Implementing It: The Internals
JavaScript didn't just add a keyword; it added a protocol. Objects become "disposable" by implementing a specific method keyed by a global Symbo.
Sync Disposal
Symbol.dispose
For closing file handles, stopping timers, or releasing memory buffers immediately.
Async Disposal
Symbol.asyncDispose
For closing DB connections, flushing logs to disk, or network socket termination (returns a Promise).
When you use the using keyword (or await using), the JavaScript engine does the following invisibly:
- Creates a hidden
try/finallyblock around the current scope. - Inside
finally, checks if the object has the disposal symbol. - Calls that method primarily to clean up artifacts.
Deep Dive: Stack Unwinding
Just like C++ destructors, resources are disposed in Reverse Order of their creation (LIFO - Last In, First Out).
If you open Connection A then Connection B, Connection B will always close first. This is critical for preventing dependency errors during shutdown.
class DatabaseConnection {'{'}
constructor(id) {'{'} this.id = id; {'}'}
// The magic method
[Symbol.asyncDispose]() {'{'}
await this.disconnect();
console.log(`Disposed Connection ${this.id}`);
{'}'}
{'}'}
04. The Senior Engineer's Take
RAII comes to JavaScript
This pattern is known as RAII (Resource Acquisition Is Initialization) in C++ and Rust. It is the gold standard for resource safety.
Killer Use Case: DB Transactions
Ever locked a database row because an error occurred before you could call COMMIT or ROLLBACK?
With explicit management, you can create a TransactionHandle that automatically rolls back if the scope exits with an error. No more deadlocks.
Symbol.dispose in older environments (e.g., core-js).