[whatwg/streams] `.throw()`ing an async iterator (Issue #1284)

The [`AsyncIterator` spec](https://tc39.es/ecma262/#table-async-iterator-optional) defines a `throw()` method.

>Invoking this method notifies the AsyncIterator object that the caller has detected an error condition. The argument may be used to identify the error condition and typically will be an exception object.

This method seems to be ignored by streams. [The Streams spec](https://streams.spec.whatwg.org/#rs-asynciterator) defines a response to `return()` but doesn't mention `throw()`.

Implementations have left it out. [`web-streams-polyfill`](https://github.com/MattiasBuelens/web-streams-polyfill/blob/a9cfbf6da57e8929cc063872eb36cdb984da6c8a/src/lib/readable-stream/async-iterator.ts) and [the Node.js implementation](https://github.com/nodejs/node/blob/de4fb11e4f29b91656b831a6fb7619c94b630626/lib/internal/webstreams/readablestream.js#L536) both define `return()` but not `throw()`. Browsers seem not to support iteration yet.

I feel like I should be receiving this signal in my streams. If the consumer has an error, they should be able to tell me so I can respond to it.

One use case: `throw()` is meaningful in a generator function. Suppose I have a series of transforms wrapping a generator. I'd like a `throw()` at the end to propagate all the way back so it can be thrown in the generator code. This hits different logic than a `return()`.

```js
async function * latestNews () {
  const connection = open()
  try {
    yield * connection.readAll()
  } catch (error) {
    connection.reportError(error)
    throw error
  } finally {
    connection.close()
  }
}

const stream = new GeneratorStream(latestNews())
  .pipeThrough(new FilterToInterestingStream())
  .pipeThrough(new RenderMarkdownStream())
  .pipeThrough(new RenderLinkPreviewsStream())

const iterator = stream[Symbol.asyncIterator]()
// next() next() next()
await iterator.throw(new Error('We crashed, shut it down!'))
```

The error might even be handled so generation can continue. This isn't possible with a `return()`.

```js
async function * latestNews () {
  const connection = open()
  while (connection.active) {
    try {
      yield await connection.readNext()
    } catch (error) {
      connection.reportError(error)
      if (error.recoverable) {
        connection.resetState()
        continue
      } else {
        connection.close()
        throw error
      }
    }
  }
}
```

`throw()` is also potentially meaningful in any custom iterators a stream might be wrapping.

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

Message ID: <whatwg/streams/issues/1284@github.com>

Received on Sunday, 11 June 2023 20:32:52 UTC