-
Notifications
You must be signed in to change notification settings - Fork 3k
Description
The ECMAScript Explicit Resource Management proposal is currently at Stage 2 and is nearing advancement Stage 3. I'd like to get a feel from WHATWG as to whether, and how, WHATWG might integrate the capabilities of this proposal into existing APIs. I've created a rough list of potential integration points in the proposal explainer.
What follows is a brief summary of the proposal. You can read more about the proposal in the proposal repository and the proposal specification text. If you are unfamiliar with the TC39 staging process, please see the TC39 process document.
The Explicit Resource Management proposal provides the ability for JavaScript users to leverage RAII ("Resource Acquisition is Initialization")-style declarations via the new using
declaration:
{
using x = acquireSomeResource();
...
} // 'x' is disposed when exiting Block
A using
declaration is constant, block-scoped declaration (like const
), and tracks its binding for disposal at the end of the containing block, regardless as to whether from a normal or abrupt completion. This allows for explicit control over the lifetime of a resource. A resource is an object with a Symbol.dispose
method that, when called, should perform synchronous cleanup activities.
This kind of declaration is extremely useful when managing the scoping and lifetime of multiple resources, as it guarantees ordered cleanup of resources (excluding process/thread termination):
{
using x = createResource1();
using y = createResource2();
...
} // disposes 'y', then disposes 'x'
In addition, this proposal introduces a new DisposableStack
constructor which allows a user to collect multiple disposable resources in a single disposable container, which is extremely helpful when composing disposable classes consisting of other resources:
class MyResource {
#x;
#y;
#disposables;
constructor() {
using stack = new DisposableStack(); // 'stack' will be disposed at end of constructor
this.#x = stack.use(createResource1()); // Add resource for '#x' to 'stack'
this.#y = stack.use(createResource2()); // Add resource for '#y' to 'stack'
// if either 'createResource1()' or 'createResource2()' throws, 'stack' and its contents
// will be disposed.
this.#disposables = stack.move(); // move resources out of 'stack' and into '#disposables'
// disposes 'stack' (now empty), but not '#x', '#y', or '#disposables'
}
...
[Symbol.dispose]() {
this.#disposables.dispose(); // disposes '#y' and '#x'
}
}
Not all resources can be disposed of in a synchronous manner, however. This proposal also introduces Symbol.asyncDispose
and an AsyncDisposableStack
API for asynchronously disposed resources. These two APIs are part of the main proposal and are intended to advance to Stage 3. However, syntax for an asynchronous using
declaration has been postponed to a follow-on proposal due to its possible dependency on async do
expressions, though it will likely look something like the following:
async function f() {
// body-scoped 'async using'
async using x = someAsyncDisposableResource1();
// block-scoped 'async using' requires an explicit 'await' marker:
await async do {
async using y = someAsyncDisposableResource2();
...
}; // performs (roughly) 'await y?.[Symbol.asyncDispose]()
} // performs (roughly) 'await x?.[Symbol.asyncDispose]()
In summary, the following features are at Stage 2 and will be proposed for advancement to Stage 3 at the upcoming TC39 plenary:
using
declarations — block-scoped, constant, synchronous disposal.Symbol.dispose
— built-in symbol, indicates disposal method.Symbol.asyncDispose
— built-in symbol, indicates async disposal method.DisposableStack
— disposable container.AsyncDisposableStack
— async disposable container.
The following features are at Stage 2 and have been postponed temporarily due to dependency on other proposals:
async using
declarations — block-scoped, constant, asynchronous disposal.