Re: [whatwg/streams] Clarify that a pull source doesn't need to wait in pull() (#1014)

I don't think we should discourage returning a promise. Imagine a `ReadableStream` that enqueues *multiple* chunks after each `pull()`:
```javascript
const rs = new ReadableStream({
  async pull(c) {
    await new Promise(r => setTimeout(r, 100));
    c.enqueue('a');
    c.enqueue('b');
  }
}, { highWaterMark: 2 });
```
After `c.enqueue('a')` is called, the stream does *not* immediately call `pull()` again since the promise from the previous pull is still pending. After the second enqueue, the queue is filled up to the HWM, so the stream no longer needs to call `pull()` again. As a result, this stream will call `pull()` only once at start-up, as expected.

Compare this to a version that does *not* return a promise:
```javascript
const rs = new ReadableStream({
  pull(c) {
    (async () => {
      await new Promise(r => setTimeout(r, 100));
      c.enqueue('a');
      c.enqueue('b');
    })();
  }
}, { highWaterMark: 2 });
```
After `c.enqueue('a')` is called, the stream immediately calls `pull()` again, since it only has 1 chunk in its queue and it wants to fill up to HWM = 2. However, it does not know that we call `c.enqueue('b')` immediately afterwards, so we end up filling up past the HWM with 4 chunks in the queue.

This is probably a bit of a contrived example. But you could have a similar problem if you need to do some cleanup after each `pull()`, for example:
```javascript
// imagine there's a hypothetical `source` somewhere
const rs = new ReadableStream({
  pull(c) {
    await source.startPulling();
    const chunk = await source.getNextChunk();
    c.enqueue(chunk);
    await source.finishPulling();
  }
});
```
By returning a promise, we ensure that `finishPulling()` is always called before the next pull calls `startPulling()` again. If we change this to *not* return a promise (by wrapping the code inside `pull()` with an async IIFE), then this is no longer guaranteed and the hypothetical `source` might break.

---

Alternatively: could we pass an `AbortSignal` to `pull()`, similar to #1015? That way, the implementer can return a (potentially long-running) promise from `pull()`, and still have a way to handle cancellation in a more timely manner.

-- 
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/1014#issuecomment-531196551

Received on Friday, 13 September 2019 11:09:35 UTC