Discard this stream as its output is no longer required.
Discard this stream as its output is no longer required. This could be used to signal the producer of this stream similarly how Future.raise used to propagate interrupts across future chains.
Although unnecessary, it's always safe to discard a fully-consumed stream.
A Future that resolves once this reader is closed upon reading of end-of-stream.
A Future that resolves once this reader is closed upon reading of end-of-stream.
If the result is a failed future, this indicates that it was not closed either by reading until the end of the stream nor by discarding. This is useful for any extra resource cleanup that you must do once the stream is no longer being used.
Asynchronously read the next element of this stream.
Asynchronously read the next element of this stream. Returned Future will resolve into
Some(e)
when the element is available or into None
when stream is exhausted.
Stream failures are terminal such that all subsequent reads will resolve in failed Futures.
Construct a new Reader by applying f
to every item read from this Reader
Construct a new Reader by applying f
to every item read from this Reader
the function constructs a new Reader[B] from the value of this Reader.read
All operations of the new Reader will be in sync with self Reader. Discarding one Reader will discard the other Reader. When one Reader's onClose resolves, the other Reader's onClose will be resolved immediately with the same value.
Converts a Reader[Reader[B]]
into a Reader[B]
Construct a new Reader by applying f
to every item read from this Reader
Construct a new Reader by applying f
to every item read from this Reader
the function transforms data of type A to B
All operations of the new Reader will be in sync with self Reader. Discarding one Reader will discard the other Reader. When one Reader's onClose resolves, the other Reader's onClose will be resolved immediately with the same value.
A reader exposes a pull-based API to model a potentially infinite stream of arbitrary elements.
Given the pull-based API, the consumer is responsible for driving the computation. A very typical code pattern to consume a reader is to use a read-loop:
One way to reason about the read-loop idiom is to view it as a subscription to a publisher (reader) in the Pub/Sub terminology. Perhaps the major difference between readers and traditional publishers, is readers only allow one subscriber (read-loop). It's generally safer to assume the reader is fully consumed (stream is exhausted) once its read-loop is run.
Error Handling
Given the read-loop above, its returned Future could be used to observe both successful and unsuccessful outcomes.
Whether or not multiple outstanding reads are allowed on a
Reader
type is an undefined behaviour but could be changed in a refinement.Cancellations
If a consumer is no longer interested in the stream, it can discard it. Note a discarded reader (or stream) can not be restarted.
,The read-loop above, for example, exhausts the stream (observes EOF) hence does not have to discard it (stream).
Back Pressure
The pattern above leverages Future recursion to exert back-pressure via allowing only one outstanding read. It's usually a good idea to structure consumers this way (i.e., the next read isn't issued until the previous read finishes). This would always ensure a finer grained back-pressure in network systems allowing the consumers to artificially slow down the producers and not rely solely on transport and IO buffering.
,Once failed, a stream can not be restarted such that all future reads will resolve into a failure. There is no need to discard an already failed stream.
Resource Safety
One of the important implications of readers, and streams in general, is that they are prone to resource leaks unless fully consumed or discarded (failed). Specifically, readers backed by network connections MUST be discarded unless already consumed (EOF observed) to prevent connection leaks.