-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
ECMAScript Explicit Resource Management proposal integration with web APIs #8557
Comments
A use case we discussed on Matrix, which I want to capture here so I don't forget about it, is to have a cancel-on-dispose AbortController, along the lines of using controller = new AbortController.AutoAbort();
let pages = await Promise.all(urls.map(url => fetch(url, { signal: controller.signal }));
// automatically cancels outstanding requests if any request fails I think that would be very useful. It's possible to do today with try-finally, as in let controller = new AbortController;
let pages;
try {
pages = await Promise.all(urls.map(url => fetch(url, { signal: controller.signal }));
} finally {
controller.abort();
} but that's pretty awkward, and I suspect the awkwardness has prevented people from adopting that pattern. (Personally I'd be fine with making |
|
My main objection to In .NET, the default behavior of In lieu of that, I would suggest the addition of an RAII-style wrapper class that could be used instead: {
const controller = new AbortController();
using scope = new AbortController.AbortScope(controller);
let pages = await Promise.all(urls.map(url => fetch(url, { signal: controller.signal }));
} // scopeis disposed, which in turn calls `controller.abort()` This provides more flexibility, as other "do-X-on-dispose" behaviors can be modeled independently as well as be composed with other objects such as // create a controller that is usable across several phases of an async operation
const controller = new AbortController();
{
using stack = new DisposeStack();
stack.use(new AbortController.AbortScope(controller));
// between here until the last statement of the block, any exception will abort the controller
...
stack.move(); // we've stepped past the point where we need to ensure we abort
}
// next phase of operation
{
using stack = new DisposableStack();
stack.use(new AbortController.CleanupScope(controller));
// between here until the last statement of the block, any exception will just result
// in cleanup
...
stack.move(); // we've stepped past the point where we want to cleanup
}
// final phase
{
using stack = new DisposableStack();
stack.use(new AbortController.AbortScope(controller));
// once again, any exception will abort the controller
// no need to move the scope out of the dispose stack now, since we're done
return;
} While this is arguably less convenient than just {
using scope = new AbortController.AbortScope(controller);
...
scope.release(); // release scope without disposing it
}
// or
{
using scope = controller.abortScope();
...
scope.release();
}
{
using scope = controller.cleanupScope();
...
scope.release();
} |
Also, if we chose to adopt a secondary symbol to "enter" a disposable, as is being discussed in tc39/proposal-explicit-resource-management#195, something like what @bakkot has suggested wouldn't even necessarily require subclassing: AbortController.abortOnDispose = function() {
const controller = new AbortController();
return {
[Symbol.enter]() { return controller; },
[Symbol.dispose]() { controller.abort(); }
};
}
using controller = AbortController.abortOnDispose();
// which is roughly equivalent to:
// const _a = AbortController.abortOnDispose();
// const controller = _a[Symbol.enter]();
// using _b = _a; |
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:A
using
declaration is constant, block-scoped declaration (likeconst
), 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 aSymbol.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):
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:Not all resources can be disposed of in a synchronous manner, however. This proposal also introduces
Symbol.asyncDispose
and anAsyncDisposableStack
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 asynchronoususing
declaration has been postponed to a follow-on proposal due to its possible dependency onasync do
expressions, though it will likely look something like the following: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.The text was updated successfully, but these errors were encountered: