[whatwg/streams] ReadableStreamBYOBReader.prototype.readFully(view) (#1143)

As suggested in [#1019 (comment)](https://github.com/whatwg/streams/issues/1019#issuecomment-565499158), developers may want to read into a BYOB buffer until it is *completely filled* (instead of only *partially* filling it with however many bytes the underlying source provides). We already have [an example for this](https://streams.spec.whatwg.org/commit-snapshots/cada8129edcc4803b2878a7a3f5e1d8325dc0c23/#example-manual-read-bytes) with a `readInto()` helper function, but it may be worth integrating this into the standard itself for better ergonomics and/or optimization opportunities.

Concretely, I suggest we extend `ReadableStreamBYOBReader`:
```
interface ReadableStreamBYOBReader {
  Promise<ReadableStreamBYOBReadResult> readFully(ArrayBufferView view);
};
```
This method performs a BYOB read into `view`:
* If the underlying source responds with `n` bytes where `n < view.byteLength`, then the stream will immediately start pulling again with the "remainder" of `view` (i.e. `view.subarray(n)`, assuming `view` is a `Uint8Array`).
* If the underlying source responds with `n` bytes where `n === view.byteLength`, then the method fulfills with `{ done: false, value }` where `value` is the completely filled view.
* If the underlying source closes before the view is completely filled, then the method fulfills with `{ done: true, value }` where `value` is the (partially) filled view.
* If the underlying source cancels, then the method returns with `{ done: true, value: undefined }`, like with regular BYOB reads.

Some remarks:
* We can't return a `Promise<ArrayBufferView>`, because then you wouldn't know if the stream is closed. Also, this wouldn't work if the stream is cancelled... unless we're okay with returning `Promise<undefined>` in that case (which I personally don't find very pretty).
* We have to be very careful with how we create a pull-into descriptor for the remainder. There may already be *other* pull-into descriptors into the queue, for example a user could do this:
  ```javascript
  let pullCount = 0;
  const rs = new ReadableStream({
    type: 'bytes',
    pull(c) {
      ++pullCount;
      c.byobRequest.view[0] = pullCount;
      c.byobRequest.respond(1);
    }
  });
  const reader = rs.getReader({ mode: 'byob' });
  const read1 = reader.readFully(new Uint8Array(3));
  const read2 = reader.read(new Uint8Array(1));
  await read1; // should be [1, 2, 3], NOT [1, 3, 4]
  await read2; // should be [4], NOT [2]
  ```
  One solution would be to have a "special type" for `readFully()` pull-into descriptors. Or another (simpler?) solution might be to always put the pull-into descriptor for the remainder *at the front of the `[[pendingPullIntos]]` queue* (rather than at the back).
* Should we also have a shorthand `ReadableStream.readFully(view)`? Would it have the same signature, i.e. returning `Promise<ReadableStreamBYOBReadResult>`?

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

Received on Monday, 12 July 2021 22:15:58 UTC