tc39 / proposal-array-from-async

Draft specification for a proposed Array.fromAsync method in JavaScript.
http://tc39.es/proposal-array-from-async/
BSD 3-Clause "New" or "Revised" License
177 stars 13 forks source link

Wiring of return value is underspecified #31

Closed bakkot closed 2 years ago

bakkot commented 2 years ago

fromAsync makes use of an Abstract Closure passed to AsyncFunctionStart. What is the return type of this closure? The use of ? implies it must be a Completion Record, but there's also a bare Return A. which returns a value not wrapped in a completion record. In normal built-in functions this would be implicitly wrapped in NormalCompletion by the rules in this clause, but a.) that doesn't apply within the Abstract Closure (rather, it's the thing which allows the final Return promiseCapability.[[Promise]] line to work and b.) that wouldn't do the right thing anyway, because the logic in AsyncBlockStart treats an invocation returning a normal completion as if it had explicitly returned undefined (which is how normal functions work) - an actual return would be a return completion.

I see three possible ways forward:

  1. in the closure within fromAsync, explicitly wrap each Return _value_. as Return Completion Record { [[Type]]: ~return~, [[Value]]: _value_, [[Target]]: empty }
  2. in AsyncFunctionStart, have a different path for Abstract Closures vs built-in functions
  3. define "built-in async function" rigorously in a way which would allow you to write the steps of a built-in function, but using Await

Of these I think the third option is cleanest, especially if we add more built-in async stuff (as the iterator helpers proposal will do).

js-choi commented 2 years ago

You mentioned that you generously might sketch the third approach in tc39/proposal-iterator-helpers#218. The proposal-iterator-helpers specification uses language such as “…is a built-in async function which, when called, performs the following steps…” But as you know, the current Array.fromAsync specification does not use that language; it instead uses an Abstract Closure passed to AsyncFunctionStart. So rigorously defining “built-in async functions” would help the proposal-iterator-helpers spec, but it would not help the current Array.fromAsync spec.

So is the third choice proposing that we rewrite the Array.fromAsync spec to use the same “…is a built-in async function which…” language as proposal-iterator-helpers? (See also #23.) But this still would require that we properly define “built-in async function”.

In the meantime, perhaps we could use the first choice as a stopgap for Array.fromAsync.

There are only two “Return A” lines in the current Array.fromAsync algorithm. We could easily replace them with “Return Completion Record { [[Type]]: ~return~, [[Value]]: A, [[Target]]: ~empty~ }”.

That may be good enough for Stage 3 (my goal for Stage 3 is the September plenary). And we could always switch its definition to use a “built-in async function” as an editorial change later, once tc39/proposal-iterator-helpers#218 gets resolved.

bakkot commented 2 years ago

So is the third choice proposing that we rewrite the Array.fromAsync spec to use the same “…is a built-in async function which…” language as proposal-iterator-helpers? (See also #23.) But this still would require that we properly define “built-in async function”.

Yes, that was the suggestion. And yeah, it is ok to do that refactoring later.