Normative: avoid triggering throw
in corner case in async generators
#2818
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Fixes #2813.
The idea is that if you are doing
yield* m
on a manual async iteratorm
(i.e., one which is not an async generator), it is possible for the inner iterator to yield{ next: promise, done: false }
. And if that promise rejects, it is treated as if someone had called.throw
on the outer generator (the one doingyield*
). That's inconsistent with the way the spec usually consumes iterators and is almost certainly a bug caused by the factoring ofAsyncGeneratorYield
. (Async generators await their result before yielding it, so it doesn't come up without a manually implemented iterator.)There's only really three consistent options, that I can see.
.throw
or.return
should be called on the inner iterator (as if calling.next
had thrown)yield*
as being a completely transparent delegation to the inner iterator). This option would also reduce the number of microtask ticks entailed by async generators doingyield*
in general. I've opened an alternative PR in Normative: avoid mostly-redundantawait
in asyncyield*
#2819 which implements this option.This PR takes the first option.
I don't really like the second option, because
for await
does not treat an async iterator yielding a promise as being a protocol violation - see this snippet.I actually kind of prefer the third option, but it has broader implications than this PR does, and so is more likely to break stuff. On the other hand, it also would improve the performance of async generators, which is nice.
As mentioned in the issue, this does not match any existing engine, but engines are inconsistent here, so the change in this PR is probably web compatible.