[streams] ready fulfilling when state ends up being "waiting" due to reading in the same turn (#263)

This is mostly for discussion, as it is probably not a bug problem. But I wanted to lay it out.

In the review of some reader fixes at https://github.com/whatwg/streams/pull/262#discussion_r22993414 @yukatahirano pointed what you could see as a potential waiting -> waiting transition. It occurs even without readers so I think we can leave them out of the discussion. An example:

```js
stream.ready.then(() => console.log(stream.state));

// enqueue a chunk
stream.read();

// will log "waiting"
```

Normally we try to enforce that when `ready` fulfills, the state is no longer `"waiting"`, since kind of the point of `ready` is to tell you when you're done waiting.

This kind of usage is atypical though so it is not too problematic. Usually you do not know, until `ready` fulfills, that the stream has become `"readable"`. In this example we are using external knowledge---namely, the fact that on the previous line we enqueued in the stream---to figure it out.

If we were to think about mitigating this, the options don't seem great:

- Delay fulfilling `ready` for a microtask; at that time check if state is now `"waiting"` (due to queue drainage) and if so don't fulfill `ready`. This kind of just shuffles the problem around though: someone could just do `Promise.resolve().then(() => stream.read())` to "fool" the check by waiting until after it happens.
- Make everything async: i.e., don't actually do the enqueue until a microtask later. So if you enqueue, the chunk will not be immediately available for `read()`. We should be able to schedule the availability of the chunk such that it happens immediately before `ready` fulfills, thus avoiding any possibility of interleaving as in the above possibility.
- Embrace sync instead of async: stop using promises and use some sort of [zalgo-esque](http://blog.izs.me/post/59142742143/designing-apis-for-asynchrony) callback system that synchronously notifies of availability. I am not sure why I am even mentioning this.

I just realized there is another way to encounter a `"waiting"` state in the `ready` handler, even when doing "normal" usage:

```js
stream.ready.then(() => {
  console.log(stream.state);
  stream.read();
});

// in some other part of the code, perhaps:
stream.ready.then(() => {
  console.log(stream.state);
});

// enqueue a single chunk

// will log "readable", "waiting"
```

basically any multi-consumer scenario has the potential to see "waiting".

So in general maybe the takeaway here is to add a quick note that usually the state will be non-"waiting", but it's possible that it will be "waiting" if someone drained the queue between the transition happening and your fulfillment handler being called. I still think it's worthwhile to preserve the pseudo-invariant in other cases though.

---
Reply to this email directly or view it on GitHub:
https://github.com/whatwg/streams/issues/263

Received on Thursday, 15 January 2015 20:33:03 UTC