Closed rbuckton closed 1 year ago
A preview of this PR can be found at https://tc39.es/proposal-async-explicit-resource-management/pr/6.
Can you using await x;
with no assignment?
This PR also cleans up a few sections of the non-async version of the proposal that I plan to backport:
IsUsingDeclaration
SDO was missingForBodyEvaluation
and CreatePerIterationEnvironment
. The DisposeResources
calls in ForBodyEvaluation
aren't necessary as the per-iteration environment created for each iteration only contains mutable bindings, so there's never anything to dispose.DisposableStack.prototype.use
can be simplified with a few small changes to AddDisposableResource
.Can you
using await x;
with no assignment?
No.
Can you using x;
with no assignment? If the two aren't in sync, why? (if they are, then, great)
Can you
using x;
with no assignment? If the two aren't in sync, why? (if they are, then, great)
No, you can't do using x;
either, just like you can't do const x;
.
I like everything about this except that it has using await
instead of async using
. I think that await
suggests the RHS would be awaited, whereas async
suggests that there is awaiting later, and so async
is more appropriate. I do not expect this to be difficult for readers to understand. And @mhofman said in that thread that either syntax would meet the requirement that "a simple syntactic glance at the source allows to realize an interleaving point does exist", so it sounds like that would be broadly acceptable given the other choices made about the semantics in this PR.
I see someone from the community has expressed the same intuition as me.
(Seems fine to merge this PR as-is and continue discussing the spelling in another issue, though, since this PR establishes more than just the spelling.)
I like everything about this except that it has
using await
instead ofasync using
. I think thatawait
suggests the RHS would be awaited, whereasasync
suggests that there is awaiting later, and soasync
is more appropriate.
I chose using await
as @erights has indicated his position that await
and yield
are the only appropriate keywords to use to indicate implicit interleaving points, and that async
is not a sufficient indicator. While that preference may have been weakened somewhat per the comment you referenced, I still think using await
is the better choice. The async
keyword today does not indicate "awaiting later", but instead indicates that the body of the function may contain await
, and that executing the function will return a Promise
. Instead, the await
in for-await-of
is a better indication of "awaiting later" (though it also "awaits now").
I also chose using await
as it more closely resembles the equivalent syntax in C# (namely await using x = ...
) as the syntactic similarity is helpful for multi-language developers. However, I chose the using await
ordering because await using
would be an ambiguous parse given that using
is a valid IdentifierReference.
Finally, using await
works more cleanly with the static semantics for ClassStaticBlockDeclaration, which has an early error for "Contains await
".
as @erights has indicated his position that
await
andyield
are the only appropriate keywords to use to indicate implicit interleaving points
I disagree with that position. I don't think developers will trip over async using
- it's easy to learn. And in this case we're indicating an interleaving point elsewhere, so it's already a somewhat different thing.
The
async
keyword today does not indicate "awaiting later", but instead indicates that the body of the function may containawait
, and that executing the function will return aPromise
.
Right - a meaning clearly inapplicable to using
. So I think it will be easy for readers to generalize to "this is the asynchronous version of this thing, where the precise meaning of 'asynchronous version' depends on the thing".
By contrast, await
today means "executing this statement will perform an await
", which is not true here. And since that's a thing which could easily apply to using
declarations (i.e., by awaiting the RHS), there's no indication to readers that this interpretation is inapplicable here. I would really prefer to avoid choosing a syntax which has an obvious interpretation which is incorrect.
I also chose using
await
as it more closely resembles the equivalent syntax in C#
async using
is about as close to await using
as using await
is, surely?
Finally,
using await
works more cleanly with the static semantics forClassStaticBlockDeclaration
, which has an early error for "Containsawait
".
As I've mentioned elsewhere, I think that the difficulty of specifying one semantics or another should basically never factor into our user-facing decisions about the language, except for things which will be encountered very rarely and which we are approximately ambivalent between - not the case here. Whatever we settle on, as long as it's implementable, as editor I am happy to find a way to write that down.
Is there a way to provoke the github actions to keep https://tc39.es/proposal-async-explicit-resource-management/pr/6/ up to date?
It already should be doing this.
Thanks! (Looking again, I'm not sure why I was confused)
Given that the actual interleaving point is no longer explicit, we do have a preference for the using await
syntax as it carries the notion there is an interleaving point better than async using
does. As @rbuckton mentions, async
is used to denote await
is syntactically available, which is weaker than "an interleaving will occur". This is however not a blocker for us as linting tools should work equally with either.
Not having given this PR a totally thorough review, it looks reasonable to me, and delivers on the promises made by parts of the README today (which is, without this, a little incoherent, as I noted in #7). I recommend that you land this patch now, as review continues, to avoid the confusion for future reviewers that I went through just now :)
(A tangent, and I'm a bit late to this, but it feels kinda crazy to me that Await
isn't "viral" to callers in spec text the way it is in JS code, especially since throwing an exception is viral. I wonder if we should change that. But not today.)
On using await
vs of async using
, as a datapoint, python uses async with
as the async version of its with
statement, which serves a similar role. That inclines me more towards async using
as the async version of using
.
On
using await
vs ofasync using
, as a datapoint, python usesasync with
as the async version of itswith
statement, which serves a similar role. That inclines me more towardsasync using
as the async version ofusing
.
Both C# and Python heavily influenced the design of this proposal. C#'s variant uses await
, and Python's variant uses async
, so leveraging prior art in this case is a bit of a wash. Instead, I'd rather leverage the usage of async
and await
in JS as the deciding factor.
Every instance of await
in JS today indicates that we should "pause execution and wait for the result". In the case of AwaitExpression, this is an immediate consequence. In the case of for-await-of
, we perform multiple "awaits" during execution:
iter.next()
at the start of each iteration.iter.return()
due to an abrupt completion (break
/continue
/throw
).As a result, for-await-of
has both an immediate consequence of its await
(iter.next()
), as well as a delayed consequence (iter.return()
).
Every instance of async
in JS today indicates "something that must eventually be "awaited" to observe its result" (in this sense, I am grouping both await
and Promise.prototype.then
as "awaiting" the result). Even proposals like async do
fully align with this meaning.
The async version of the using
syntax more closely aligns with the delayed consequence behavior of for-await-of
, thus I think it is important that the await
keyword be involved in the syntax. When I brought async using
up for discussion, it was intentionally paired with a using await {}
block that still used the await
keyword as an indicator. The async
keyword in that case was purely to differentiate that version of using
from the sync version of using
.
using await
as a standalone declaration more closely aligns with the existing meaning of await
in JS, and ensures we're still using await
to call out that a "pause execution and wait for the result" operation is still in effect, even if the consequence itself is delayed. As such, I still plan to go to plenary with the using await
syntax.
If necessary, we can bikeshed the use of await
or async
in a separate issue thread, or during plenary.
This updates the proposal specification text to reflect the tentative outcome of #1:
using
isusing await x = y
.A Block containing a
using await
willawait
when control flow exits the block if anyusing await
statements are evaluated, even if the resource wasnull
orundefined
:Fixes #1