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
179 stars 13 forks source link

Spec Issue: IteratorStep returns a promise for async iterators, leading to an infinite loop #33

Closed mgaudet closed 10 months ago

mgaudet commented 2 years ago

In the published spec, Step 3.j.ii.3 and 4 are the termination condition of the iteration:

The problem is that IteratorStep doesn't properly flag termination on an async iterator; the return value of the IteratorNext in async iteration is a Promise, which doesn't have a "done" property, and so we always get the promise object from IteratorStep.

I think the patch is actually relatively simple; Step 3.j.ii.4 should be expanded into the steps which have the effect of "if next.done is false:"

bakkot commented 2 years ago

Good catch. The fix is a little more complicated than your suggestion, though. IteratorStep isn't usable at all with async iterators; it performs IteratorComplete on the result of the next method, which assumes the return value of that method is an iterator result, not a promise for one.

I think instead we want to invoke the next method directly, like for await does (which is currently one of the only consumers of the async iteration protocol):

1. Let nextResult be ? Call(iteratorRecord.[[NextMethod]], iteratorRecord.[[Iterator]]).
1. Set nextResult to ? Await(nextResult).
1. If nextResult is not an Object, throw a TypeError exception.
1. Let done be ? IteratorComplete(nextResult).
1. If done is true,
  1. Perform ? Set(A, "length", 𝔽(k), true).
  1. Return Completion Record { [[Type]]: return, [[Value]]: A, [[Target]]: empty }.
1. Let nextValue be ? IteratorValue(nextResult).
[etc]

Alternatively, I suppose we could have IteratorStep take an option "async" flag which would cause it to do the awaiting itself. Something to worry about during the stage 4 PR, possibly.

mgaudet commented 1 year ago

Note: I'm preparing to ship Array.fromAsync. It will ship using the above spec-patch semantics.