Closed domenic closed 8 years ago
Sketching this out also revealed a few questions. I think this is the correct way to drive development and will be using the below to rewrite the spec's state for clarity and robustness. Please look this over.
[[push]]
, [[finish]]
, [[error]]
: called by start
or pull
constructor parameters.read()
, abort()
, waitForReadable()
external operations.[[push]]
: moves to readable[[error]]
: moves to errored[[finish]]
: moves to finishedread()
: no state change
abort()
: moves to finished
[[abort]]
side effectwaitForReadable()
: no state change
pull
side effect (see #26)[[push]]
: no state change
[[error]]
: moves to errored (see discussion in #25)
[[finish]]
: no state change
read()
:
abort()
: moves to finished
[[abort]]
side effectwaitForReadable()
: no state change[[push]]
: no state change
[[error]]
: no state change
[[finish]]
: no state change
read()
: no state change
abort()
: no state change
[[abort]]
anyway?waitForReadable()
: no state change[[push]]
: no state change
[[error]]
: no state change
[[finish]]
: no state change
read()
: no state change
abort()
: no state change
waitForReadable()
: no state changeFirst off, I really like this style of spec. I write state machines all the time and it's really natural for me. Also it forces you to see all the edge cases formally.
abort()
should be ignored. The consumer doesn't care what the producer does with it.I'm not quite clear on what waitForReadable()
actually does. I assume it will return a promise or take a callback to notify the caller when the stream is readable.
I'm not quite clear on what waitForReadable() actually does. I assume it will return a promise or take a callback to notify the caller when the stream is readable.
Right, I left out what it actually does. You're right that's what it's supposed to do. I need to add side effects internally to reset, resolve, or reject the internal promise it returns at the appropriate steps.
Another point: I need to prevent calling pull
more than once. This may need another state ("reading?"), or perhaps just a flag.
I’ll just leave this here. https://github.com/tabatkins/railroad-diagrams/
I like this way of visualizing through what we call marble diagrams in Rx: http://netflix.github.io/RxJava/javadoc/
Deep link to a page with the marble diagrams: http://netflix.github.io/RxJava/javadoc/rx/Observable.html and one of the wiki pages with them: https://github.com/Netflix/RxJava/wiki/Combining-Observables
I very much want dynamic "Chrome/Webkit Profiler" style visualizations (with relationship between streams added) for these types of systems. That's something we are exploring for tracing live systems.
I see a few implementation questions that may or may not need to be addressed here, but I'll bring them up.
@trevnorris thanks for your thoughts; really great to have you join the conversation.
stream.waitForReadable().then(onReadable)
will only ever call onReadable
once, when the stream is the drained state.push
always returns false
, i.e. the high water mark is effectively 0. But easy high water marks are planned, with some default easy-to-install strategies like "length
counter" (for string and ArrayBuffer
streams), or "counter" (for object streams), or customizable ones.stream.finished.then(noMoreDataBecauseSuccess, noMoreDataBecauseError)
getNext
example for getting the next element with a callback, or the readableStreamToArray
example for getting all elements; clearly you could create a hybrid that waits for N elements or N bytes before fulfilling the returned promise. User space is probably the place for building such helpers; our goal with the synchronous read interface is to provide the most performant and low-level primitive.[[finish]]
is not to immediately transition into the finished state, but to set a finished flag so that no more data can be written, and once the buffer is trained you transition into the finished state.I rewrote things to use a state machine. I think it might be possible to remove the "pulling," "draining," and "opening" flags in favor of more internal states (i.e. ones not exposed through readableState
). But this at least fixes a lot of issues and edge cases. Time to move on to writable stream and the toolbox.
Here is a draft of the state machine for readable stream:
http://www.gliffy.com/go/publish/5148129
Feedback welcome. Maybe todo:
++
Two writable stream ideas, one with loopbacks:
http://www.gliffy.com/go/publish/6388889
one without:
http://www.gliffy.com/go/publish/6388970
Could add loopbacks to readable stream too, unsure.
Would be interested in review before SVG-izing this.
The initial state is "Waiting" in the diagram but is "writable"
in the spec. Let's discuss in #242
This would be an immensely helpful illustrative diagram to make.