[whatwg/streams] ReadableStreamDefaultController seems redundant (#743)

Hi,

This is feedback on this API, after spending the day partially implementing it. It may be way too late for this kind of feedback, but I thought it would be better to post my thoughts than not post. Feel free to ignore me.

I've spent a lot of time in the past thinking about streams and the different interfaces they can have (e.g. when working on Protobufs and Cap'n Proto which care about this kind of thing). I'm impressed that this API seems to cover a lot of obscure cases that people often don't think about, e.g. push vs. pull, backpressure, producer-allocated vs. consumer-allocated buffers, etc.

However, the result feels overcomplicated, and I wonder if it could be simplified.

I wonder if the concept of a "controller" is redundant. I note that ReadableStream's "controller" is _almost_ just a WritableStream, and I wonder if these could actually be unified.

Currently, ReadableStream's constructor takes an `underlyingSource` with three (optional) methods: `start`, `pull`, and `cancel`. What if instead of `start` and `pull`, it just provided `pipeTo`, with the same signature as `ReadableStream#pipeTo`?

The ReadableStream would not call this callback until some data is first requested. At that time, it can do different things depending on the request:

* If `getReader()` is called, then the ReadableStream constructs a WritableStream that places chunks in a queue, which the `Reader` then pulls from. This WritableStream's write() method would return a promise which resolves when the queue is ready for more chunks (as evaluated using the queuing strategy, etc.), to provide backpressure. Having constructed this WritableStream, it is passed to the `pipeTo` callback that was given to ReadableStream's constructor.

* On the other hand, if the ReadableStream's own `pipeTo()` method is called instead of `getReader()`, it can trivially call the `pipeTo` callback passing along the same target stream. The ReadableStream can then step out of the picture entirely! This is really nice because it avoids double-buffering, and it gives the original producer the opportunity to query the output stream with `instanceof`, possibly discovering a more-efficient way to implement the pipeline (a common thing to want to do, in my experience!).

The major thing missing from this approach is support for BYOB. I think this could be answered by just throwing `byobRequest()` onto WritableStream itself. Or, perhaps a bit more intuitively, the WritableStream interface could be extended with a method like `nextBuffer()` which returns a buffer to use, or null if the WritableStream doesn't care. The producer would then be expected to pass that buffer back into `write()` -- although they'd also have the option to ignore it and call `write()` directly. The implementation of `ReadableStreamBYOBReader` would detect this and perform the copy if necessary.

Thoughts?

-- 
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/743

Received on Wednesday, 10 May 2017 02:04:02 UTC