So why is this supported in the language? To continue with the sloppy promise semantics.
I think we should fall back to Symbol.iterator
because our current
Promise semantics are all about allowing sync things to be used as
async things. You might call this "sloppiness". It follows
@groundwater's logic above,
but I just want to spell out the parallels in more detail.
The "chaining" semantics of .then
are all about this. You can return a
Promise from .then
or a scalar value; it's all the same. You call
Promise.resolve
not to wrap something in a Promise, but to cast
something to a Promise--get an asynchronous value when you have
something-or-other.
The semantics of async
and await
are all about being sloppy as well.
You can slap await
on any non-Promise expression in an async function
and everything works fine, exactly the same way, except that you yield
control to the job queue. Similarly, you can "defensively" put async
around whatever you want, as long as you await
the result. If you have
a function that returns a Promise--whatever! you can make that an
async
function, and, from a user perspective, nothing changes (even
if, technically, you get a different Promise object out).
Async iterators and generators should work the same way. Just like you
can await a value that, accidentally, wasn't a Promise, a reasonable
user would expect to be able to yield*
a sync iterator within an async
generator. for await
loops should similarly "just work" if a user
defensively marks a loop that way, thinking that they maybe might be
getting an async iterator.
I think it would be a big deal to break all of these parallels. It
would make async iterators less ergonomic. Let's discuss this the next
time async generators/iterators come up on the agenda at TC39.