[whatwg/webidl] async iteration can produce promises, which the ES spec generally avoids (Issue #1288)

Consider:

```js
(async () => {
  let stream = new ReadableStream(
    {
      start(controller) {
          controller.enqueue(Promise.resolve(0)); // NOTE: enqueuing a promise
          controller.close();
      },
    },
  );
  for await (let item of stream) {
    console.log({ item });
  }
})();
```

(Note that ReadableStreams are async iterable as of https://github.com/whatwg/streams/pull/980, but that's only implemented in Firefox as of this writing.)

The [Asynchronous iterable declarations](https://webidl.spec.whatwg.org/#es-asynchronous-iterable) and [Asynchronous iterator prototype object](https://webidl.spec.whatwg.org/#es-asynchronous-iterator-prototype-object) sections in webidl describe the relevant wiring for async iteration. From what I can tell, per step 8.5.4.4 of [this algorithm](https://webidl.spec.whatwg.org/#es-asynchronous-iterator-prototype-object), there is no unwrapping for promises. This is in contrast to ES async generators, which (as you can see in [the definition of Yield](https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-yield)) will unwrap Promises before yielding them.

That means iterating over a ReadableStream, or any other async iterable which can produce Promises, will let you observe a Promise in a `for await` loop. That's arguably a contract violation, per [the original design of async iteration](https://docs.google.com/presentation/d/1U6PivKbFO0YgoFlrYB82MtXf1ofCp1xSVOODOvranBM/edit#slide=id.g223fba4116_0_196).

Possibly webidl should enforce that Promises are unwrapped here, like async generators do. (See [brief discussion in #WHATWG](https://matrixlogs.bakkot.com/WHATWG/2023-04-05#L5).)

Though note that there might be some complexity about how to handle rejected Promises - `for await` treats promise rejection as the iterable closing itself, which means it doesn't call the `return` method, which would prevent running the [asynchronous iterator return](https://webidl.spec.whatwg.org/#asynchronous-iterator-return) steps (if any) to do cleanup. So if you go this route, it's possible that unwrapping a rejected Promise will need to explicitly trigger those steps to ensure cleanup happens.

-- 
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/webidl/issues/1288
You are receiving this because you are subscribed to this thread.

Message ID: <whatwg/webidl/issues/1288@github.com>

Received on Wednesday, 5 April 2023 01:15:33 UTC