Symbol.dispose
Marks the cleanup method that a using declaration calls when a variable leaves scope.
Symbol.dispose
// as implemented on an object:
[Symbol.dispose]()Return value
symbolThe well-known symbol used as the property key for an object's synchronous cleanup method. The method's return value is ignored.
Errors
Throwing from [Symbol.dispose]() signals the resource could not be freed; using propagates that error, wrapping any error already in flight in a SuppressedError.
Changelog
- PHP Explicit Resource ManagementIntroduced with the using declaration, DisposableStack and SuppressedError. MDN lists it as limited availability — it does not yet work in some widely used browsers.
Symbol.dispose is the key for an object's synchronous cleanup method. A using declaration reads it off the initializer and calls it automatically when the enclosing block exits — by normal completion, return, break or a thrown error:
{
using file = openFile("a.txt");
read(file);
} // file[Symbol.dispose]() runs hereWhen to reach for it
Implement it on anything that holds something needing release: a file or socket handle, a lock, a subscription, a temporary DOM listener, a database transaction. It replaces the hand-written try/finally pyramid, and disposal happens in reverse declaration order so resources unwind like a stack.
What trips people up
Support is still limited — MDN does not list it as Baseline, so check your engines or transpile. The method must be synchronous: using does not await a returned promise, so asynchronous teardown needs Symbol.asyncDispose and await using. Make it idempotent, since a second call should not throw. And using is block-scoped only: it is not allowed at the top level of a script, in a for...in head, or as a bare class field.
Example
When to use it
- Release a file handle, socket or lock the moment its block ends
- Unsubscribe from an event source without a try/finally wrapper
- Roll back a database transaction automatically on an early return or throw
Frequently asked questions
What is the difference between Symbol.dispose and Symbol.asyncDispose?
Symbol.dispose is called synchronously by a using declaration and its return value is ignored, while Symbol.asyncDispose is called by await using and its returned promise is awaited. Use the async version whenever cleanup itself needs to await something.
Can I use `using` at the top level of a module?
Only inside a block. A using declaration must appear in a block, function body or module body scope where disposal has a clear end point; it is rejected in a script's top level and in for...in or for...of heads.
Does Symbol.dispose work in all browsers?
No. MDN lists it as limited availability rather than Baseline, so check the compatibility table for your targets or transpile the using syntax; the polyfill line Symbol.dispose ??= Symbol('Symbol.dispose') keeps library code interoperable.
More examples
What a using declaration does for you
The output shows the body running first, then "dispose b" before "dispose a" — using unwinds resources in reverse declaration order, exactly as this manual try/finally does.
Symbol.dispose ??= Symbol("Symbol.dispose");
const make = (name) => ({
name,
[Symbol.dispose]() {
console.log("dispose", name);
},
});
// Equivalent to: { using a = make("a"); using b = make("b"); ... }
const a = make("a");
const b = make("b");
try {
console.log("body", a.name, b.name);
} finally {
b[Symbol.dispose]();
a[Symbol.dispose]();
}Detecting disposable objects
The shape check is the same one a using declaration performs. The final lines confirm the well-known symbol carries a description but, like all well-known symbols, has no global registry key.
Symbol.dispose ??= Symbol("Symbol.dispose");
const isDisposable = (v) =>
v != null && typeof v[Symbol.dispose] === "function";
console.log(isDisposable({ [Symbol.dispose]() {} }));
console.log(isDisposable({}));
console.log(isDisposable(null));
console.log(Symbol.dispose.description);
console.log(Symbol.keyFor(Symbol.dispose));
Discussion