1 | # Synopsis
|
2 |
|
3 | In Pull-Streams, there are two fundamental types of streams `Source`s and `Sink`s. There are two composite types of streams `Through` (aka transform) and `Duplex`. A Through Stream is a sink stream that reads what goes into the Source Stream, it can also be written to. A duplex stream is a pair of streams (`{Source, Sink}`) streams.
|
4 |
|
5 | # Pull-Streams
|
6 | ## Source Streams
|
7 |
|
8 | A Source Stream (aka readable stream) is a asynchronous function that may be called repeatedly until it returns a terminal state. Pull-streams have back pressure, but it is implicit instead of sending an explicit back pressure signal. If a source
|
9 | needs the sink to slow down, it may delay returning a read. If a sink needs the source to slow down, it just waits until it reads the source again.
|
10 |
|
11 | For example, the Source Stream `fn(abort, cb)` may have an internal implementation that will read data from a disk or network. If `fn` is called with the first argument (`abort`) being truthy, the callback will be passed `abort` as it's first argument. The callback has three different argument configurations...
|
12 |
|
13 | 1. `cb(null, data)`, indicates there there is data.
|
14 | 2. `cb(true)`, indicates the stream has ended normally.
|
15 | 3. `cb(error)`, indicates that there was an error.
|
16 |
|
17 | The read method *must not* be called until the previous call has returned, except for a call to abort the stream.
|
18 |
|
19 | ### End
|
20 | The stream may be terminated, for example `cb(err|end)`. The read method *must not* be called after it has terminated. As a normal stream end is propagated up the pipeline, an error should be propagated also, because it also means the end of the stream. If `cb(end=true)` that is a "end" which means it's a valid termination, if `cb(err)` that is an error.
|
21 | `error` and `end` are mostly the same. If you are buffering inputs and see an `end`, process those inputs and then the end.
|
22 | If you are buffering inputs and get an `error`, then you _may_ throw away that buffer and return the end.
|
23 |
|
24 | ### Abort
|
25 | Sometimes it's the sink that errors, and if it can't read anymore then we _must_ abort the source. (example, source is a file stream from local fs, and sink is a http upload. prehaps the network drops or remote server crashes, in this case we should abort the source, so that it's resources can be released.)
|
26 |
|
27 | To abort the sink, call read with a truthy first argument. You may abort a source _before_ it has returned from a regular read. (if you wait for the previous read to complete, it's possible you'd get a deadlock, if you a reading a stream that takes a long time, example, `tail -f` is reading a file, but nothing has appended to that file yet).
|
28 |
|
29 | When a stream is aborted during a read, the callback provided to the read function *must* be called first, with an error, and then the abort callback.
|
30 |
|
31 | ## Sink Streams
|
32 |
|
33 | A Sink Stream (aka writable stream) is a function that a Source Stream is passed to. The Sink Stream calls the `read` function of the Source Stream, abiding by the rules about when it may not call.
|
34 |
|
35 | ### Abort
|
36 | The Sink Stream may also abort the source if it can no longer read from it.
|
37 |
|
38 | ## Through Streams
|
39 |
|
40 | A through stream is a sink stream that returns another source when it is passed a source.
|
41 | A through stream may be thought of as wrapping a source.
|
42 |
|
43 | ## Duplex Streams
|
44 |
|
45 | A pair of independent streams, one Source and one Sink. The purpose of a duplex stream is not transformation of the data that passes though it. It's meant for communication only.
|
46 |
|
47 | # Composing Streams
|
48 |
|
49 | Since a Sink is a function that takes a Source, a Source may be fed into a Sink by simply passing the Source to the Sink.
|
50 | For example, `sink(source)`. Since a transform is a Sink that returns a Source, you can just add to that pattern by wrapping the source. For example, `sink(transform(source))`. This works, but it reads from right-to-left, and we are used to left-to-right.
|
51 |
|
52 | A method for creating a left-to-rihght reading pipeline of pull-streams. For example, a method could implement the following interface...
|
53 |
|
54 | ```
|
55 | pull([source] [,transform ...] [,sink ...])
|
56 | ```
|
57 |
|
58 | The interface could alllow for the following scenarios...
|
59 |
|
60 | 1. Connect a complete pipeline: `pull(source, transform,* sink)` this connects a source to a sink via zero or more transforms.
|
61 |
|
62 | 2. If a sink is not provided: `pull(source, transform+)` then pull should return the last `source`,
|
63 | this way streams can be easily combined in a functional way.
|
64 |
|
65 | 3. If a source is not provided: `pull(transform,* sink)` then pull should return a sink that will complete the pipeline when
|
66 | it's passed a source. `function (source) { return pull(source, pipeline) }`
|
67 | If neither a source or a sink are provided, this will return a source that will return another source (via 2) i.e. a through stream.
|