Reduce unnecessary Awaits in blocks containing await using#216
Reduce unnecessary Awaits in blocks containing await using#216
await using#216Conversation
|
A preview of this PR can be found at https://tc39.es/proposal-explicit-resource-management/pr/216. |
|
Hm. I would lightly prefer to always |
bakkot
left a comment
There was a problem hiding this comment.
Does what it says, though I'm not totally convinced by the semantics as mentioned above.
|
I can be convinced to only skip for |
|
As I mentioned on the issue, skipping new turn altogether in exceptional cases is not acceptable. The rule that should be respected is that if there is an |
It should be clarified that must execute is too strong here. If all disposals throw synchronously and the surrounding block is contained within a try {
const fail = () => { throw "sync throw" };
HAPPENS_BEFORE
await fail();
}
catch {
HAPPENS_AFTER
}Even with the Skipping the new turn in exceptional cases is directly in line with existing ECMASCript. |
|
Also, the current specification text skips new turns for synchronous exceptions, as do If that were not the case, This does, however, ensure that an |
I don't believe this is equivalent. In the To be more clear, if execution proceeded past the |
|
In the The closest example of this is In this example, the synchronous throw in So this is completely in line with the behavior of both That said, the most likely implementation of a normal Even with that though, a hand-written |



Per #196 (comment), this reduces the number of
Awaits in blocks containingawait usingand inAsyncDisposableStackto only those necessary to meet the requirement that exiting a block in which anawait usingis evaluated should alwaysAwaitat least once, even if the resource defined byawait usingisnull,undefined, or synchronous disposable (i.e., an object with a@@disposeinstead of an@@asyncDispose).As currently specified, the following code results in three
Awaits when only one is necessary:This was also the case in an
AsyncDisposableStackcontaining synchronous disposables:This PR changes this behavior such that we only need an implicit
Awaitin the following cases:await using, to the disposal of a sync resource3 declared in ausing.Example 1
Here,
Bhas an implicitAwaitfor its disposal, which means (per current proposal text):B[@@asyncDispose]()is invoked in the same turn asHAPPENS_BEFORE.A[@@dispose]()is invoked in the following turn (unlessB[@@asyncDispose]()threw synchronously).HAPPENS_AFTERhappens in the same turn asA[@@dispose]().Example 2
Here,
AandBboth have implicitAwaits, which means (per current proposal text):B[@@asyncDispose]()is invoked in the same turn asHAPPENS_BEFORE.A[@@dispose]()is invoked in the following turn (unlessB[@@asyncDispose]()threw synchronously).HAPPENS_AFTERhappens in the following turn (unlessA[@@dispose]()threw synchronously).Example 3
Here, only
Aneeds to have an implicitAwait, sinceBwill dispose synchronously, which means (proposed change):B[@@dispose]()is invoked in the same turn asHAPPENS_BEFORE.A[@@asyncDispose]()is invoked in the same turn asB[@@dispose]().HAPPENS_AFTERhappens in the following turn (unless bothB[@@dispose]()andA[@@asyncDispose]()threw synchronously).Note that this means that the above could all occur synchronously if both
B[@@dispose]()andA[@@asyncDispose]()were to throw synchronously, though that is consistent withawaitin general. However, ifA[@@asyncDispose]()were to throw synchronously butB[@@dispose]()were to complete synchronously we would need to introduce an implicitAwaitto ensure we account for the successful disposal.This PR also removes unnecessary
Awaits fromAsyncDisposableStack.prototype.disposeAsync()since you must eitherawaitor invoke.then()on the result to observe its effects.Fixes #196
Fixes #208
Footnotes
An async resource is an object with an
@@asyncDisposemethod initialized in anawait usingdeclaration. ↩An sync-from-sync resource is either
null,undefined, or an object with a@@disposemethod (but not an@@asyncDisposemethod) initialized in anawait usingdeclaration. ↩A sync resource is either
null,undefined, or an object with a@@disposemethod initialized in ausingdeclaration. ↩