An excerpt from the repository, fetched Nov 12, 2026.
Read the project’s introduction
ECMAScript Explicit Resource Management
NOTE: This proposal has subsumed the Async Explicit Resource Management
proposal. This proposal repository should be used for further discussion of both sync and async of explicit resource
management.
This proposal intends to address a common pattern in software development regarding
the lifetime and management of various resources (memory, I/O, etc.). This pattern
generally includes the allocation of a resource and the ability to explicitly
release critical resources.
For example, ECMAScript Generator Functions and Async Generator Functions expose this pattern through the
return method, as a means to explicitly evaluate finally blocks to ensure
user-defined cleanup logic is preserved:
// sync generators
function * g() {
const handle = acquireFileHandle(); // critical resource
try {
...
}
finally {
handle.release(); // cleanup
}
}
const obj = g();
try {
const r = obj.next();
...
}
finally {
obj.return(); // calls finally blocks in `g`
}
// async generators
async function * g() {
const handle = acquireStream(); // critical resource
try {
...
}
finally {
await stream.close(); // cleanup
}
}
const obj = g();
try {
const r = await obj.next();
...
}
finally {
await obj.return(); // calls finally blocks in `g`
}
As such, we propose the adoption of a novel syntax to simplify this common pattern:
// sync disposal
function * g() {
using handle = acquireFileHandle(); // block-scoped critical resource
} // cleanup
{
using obj = g(); // block-scoped declaration
const r = obj.next();
} // calls finally blocks in `g`
// async disposal
async function * g() {
using stream = acquireStream(); // block-scoped critical resource
...
} // cleanup
{
await using obj = g(); // block-scoped declaration
const r = await obj.next();
} // calls finally blocks in `g`
In addition, we propose the addition of two disposable container objects to assist
with managing multiple resources:
DisposableStack — A stack-based container of disposable resources.
AsyncDisposableStack — A stack-based container of asynchronously disposable resources.
Status
Stage: 3
Champion: Ron Buckton (@rbuckton)
Last Presented: March, 2023 (slides,
notes #1,
notes #2)
For more information see the TC39 proposal process.
Authors
Ron Buckton (@rbuckton)
Motivations
This proposal is motivated by a number of cases:
Inconsistent patterns for resource management:
ECMAScript Iterators: iterator.return()
WHATWG Stream Readers: reader.releaseLock()
NodeJS FileHandles: handle.close()
Emscripten C++ objects handles: Module._free(ptr) obj.delete() Module.destroy(obj)
Avoiding common footguns when managing resources:
const reader = stream.getReader();
...
reader.releaseLock(); // Oops, should have been in a try/finally
Scoping resources:
const handle = ...;
try {
... // ok to use `handle`
}
finally {
handle.close();
}
// not ok to use `handle`, but still in scope
Avoiding common footguns when managing multiple resources:
const a = ...;
const b = ...;
try {
...
}
finally {
a.close(); // Oops, issue if `b.close()` depends on `a`.
b.close(); // Oops, `b` never reached if `a.close()` throws.
}
Avoiding lengthy code when managing multiple resources correctly:
// sync disposal
{ // block avoids leaking `a` or `b` to outer scope
const a = ...;
try {
const b = ...;
try {
...
}
finally {
b.close(); // ensure `b` is closed before `a` in case `b`
// depends on `a`
}
}
finally {
a.close(); // ensure `a` is closed even if `b.close()` throws
}
}
// both `a` and `b` are out of scope
Compared to:
// avoids leaking `a` or `b` to outer scope
// ensures `b` is disposed before `a` in case `b` depends on `a`
// ensures `a` is disposed even if disposing `b` throws
using a = ..., b = ...;
...
// async sync disposal
{ // block avoids leaking `a` or `b` to outer scope
const a = ...;
try {
const b = ...;
try {
...
}
finally {
await b.close(); // ensure `b` is closed before `a` in case `b`
// depends on `a`
}
}
finally {
await a.close(); // ensure `a` is closed even if `b.close()` throws
}
}
// both `a` and `b` are out of scope
Compared to:
// avoids leaking `a` or `b` to outer scope
// ensures `b` is disposed before `a` in case `b` depends on `a`
// ensures `a` is disposed even if disposing `b` throws
await using a = ..., b = ...;
...
Non-blocking memory/IO applications:
import { ReaderWriterLock } from "...";
const lock = new ReaderWriterLock();
export async function readData() {
// wait for outstanding writer and take a read lock
using lockHandle = await lock.read();
... // any number of readers
await ...;
... // still in read lock after `await`
} // release the read lock
export async function writeData(data) {
// wait for all readers and take a write lock
using lockHandle = await lock.write();
... // only one writer
await ...;
... // still in write lock after `await`
} // release the write lock
Potential for use with the Fixed Layout Objects Proposal and
shared struct:
// main.js
shared struct class SharedData {
ready = false;
processed = false;
}
const worker = new Worker('worker.js');
const m = new Atomics.Mutex();
const cv = new Atomics.ConditionVariable();
const data = new SharedData();
worker.postMessage({ m, cv, data });
// send data to worker
{
// wait until main can get a lock on 'm'
using lck = m.lock();
// mark data for worker
data.ready = true;
console.log("main is ready");
} // unlocks 'm'
// notify potentially waiting worker
cv.notifyOne();
{
// reacquire lock on 'm'
using lck = m.lock();
// release the lock on 'm' and wait for the worker to finish processing
cv.wait(m, () => data.processed);
} // unlocks 'm'
// worker.js
onmessage = function (e) {
const { m, cv, data } = e.data;
{
// wait until worker can get a lock on 'm'
using lck = m.
Read the full README on GitHub