tc39 / proposal-async-iteration

Asynchronous iteration for JavaScript
https://tc39.github.io/proposal-async-iteration/
MIT License
858 stars 44 forks source link

Are suspended async generators supposed to resume synchronously? #133

Closed brainkim closed 4 years ago

brainkim commented 5 years ago

I’m sure this is answered somewhere in the spec, but are async generators supposed to resume asynchronously even if there are no await calls?

Here’s how sync generators resume:

function *syncGen() {
  console.log(yield 1);
  console.log(yield 2);
  console.log(yield 3);
  return 4;
}

const gen = syncGen();
console.log(gen.next(-1));
console.log(gen.next(-2));
console.log(gen.next(-3));
console.log(gen.next(-4));

Logs:

{value: 1, done: false}
-2
{value: 2, done: false}
-3
{value: 3, done: false}
-4
{value: 4, done: true}

Iterator result logs and yield logs are interleaved, because the suspended generator is resumed synchronously.

Compare this to how async generators resume:

async function *asyncGen() {
  console.log(yield 1);
  console.log(yield 2);
  console.log(yield 3);
  return 4;
}

const gen = asyncGen() ;
console.log(gen.next(-1));
console.log(gen.next(-2));
console.log(gen.next(-3));
console.log(gen.next(-4));

Logs:

Promise {status: "pending"}
Promise {status: "pending"}
Promise {status: "pending"}
Promise {status: "pending"}
-2
-3
-4

Note that we do not await each next result and simply call next synchronously four times. Also note that there are no await expressions in the async generator. Why does the async generator example seem to resume asychronously? Is this a necessary result of the implementation? Can someone point to the part of the spec which defines this behavior?

Thanks!

caitp commented 5 years ago

yield "awaits" the operand implicitly (https://tc39.es/ecma262/#sec-asyncgeneratoryield step 5)

brainkim commented 5 years ago

Ah okay. I figured that yield awaits the operand because of the promise-unwrapping behavior of async generators, but it didn’t really click for me that this would affect the execution of the rest of the generator body. I was looking at AsyncGeneratorResumeNext and nothing there seemed to imply asynchrony.

I guess this makes sense, but I’m trying to wrap my head around why the generator has to await the yielded value before continuing. I guess it’s because value can be an abrupt completion (rejected promise?), and it would be unexpected if the generator was resumed even after yielding an abrupt completion.