So it's possible to have resources that define both [Symbol.dispose] and [Symbol.disposeAsync]:
const fileResource = {
[Symbol.dispose]: () => { fs.closeSync(fd); },
[Symbol.disposeAsync]: async () => {
return await new Promise((resolve) => {
fs.close(fd, () => resolve());
});
}
}
Within a block we could use either using or await using to pick which disposal method to use:
{
using fileResourceSync = makeFileResource();
await using fileResourceAsync = makeFileResource();
}
However under the semantics of asyncDisposableStack.use, it will always use [Symbol.disposeAsync] regardless of which we might wish to use.
Furthermore (although less important IMO) is the fact that even if [Symbol.disposeAsync] is not on the resource, a microtask will occur between successive sync cleanups, which could be observed:
asyncDisposableStack.use({
[Symbol.dispose]: () => {
console.log("Cleanup res1");
Promise.resolve().then(() => console.log("Before cleanup res2"));
},
});
asyncDisposableStack.use({
[Symbol.dispose]: () => {
console.log("Cleanup res2");
},
});
I'd like to propose that we have an additional .useSync method on AsyncDisposableStack so that we choose to use [Symbol.dispose] even if [Symbol.disposeAsync] for the resource were available.
(NOTE: I am not proposing that asyncDisposableStack.dispose() changes in any-way, this would still unconditionally return a promise).
This would be a fairly minimal addition, the spec text is pretty much the same as .use just with the other hint:
AsyncDisposableStack.prototype.use( value )
When the use function is called with one argument, the following steps are taken:
1. Let asyncDisposableStack be the this value.
2. Perform ? [RequireInternalSlot](https://tc39.es/ecma262/#sec-requireinternalslot)(asyncDisposableStack, [[AsyncDisposableState]]).
3. If asyncDisposableStack.[[AsyncDisposableState]] is disposed, throw a ReferenceError exception.
4. Perform ? [AddDisposableResource](https://tc39.es/proposal-async-explicit-resource-management/#sec-adddisposableresource-disposable-v-hint-disposemethod)(asyncDisposableStack.[[DisposeCapability]], value, sync-dispose).
5. Return value.
So it's possible to have resources that define both
[Symbol.dispose]and[Symbol.disposeAsync]:Within a block we could use either
usingorawait usingto pick which disposal method to use:However under the semantics of
asyncDisposableStack.use, it will always use[Symbol.disposeAsync]regardless of which we might wish to use.Furthermore (although less important IMO) is the fact that even if
[Symbol.disposeAsync]is not on the resource, a microtask will occur between successive sync cleanups, which could be observed:I'd like to propose that we have an additional
.useSyncmethod onAsyncDisposableStackso that we choose to use[Symbol.dispose]even if[Symbol.disposeAsync]for the resource were available.(NOTE: I am not proposing that
asyncDisposableStack.dispose()changes in any-way, this would still unconditionally return a promise).This would be a fairly minimal addition, the spec text is pretty much the same as
.usejust with the other hint: