ReadableStream
Background
A ReadableStream is returned by the readable property inside TransformStream. On the Workers ecosystem, ReadableStream cannot be created directly using the ReadableStream constructor.
Properties
lockedboolean- A Boolean value that indicates if the readable stream is locked to a reader.
Methods
pipeTo(destinationWritableStream, optionsPipeToOptions):Promise<void>- Pipes the readable stream to a given writable stream
destinationand returns a promise that is fulfilled when thewriteoperation succeeds or rejects it if the operation fails.
- Pipes the readable stream to a given writable stream
getReader(optionsObject):ReadableStreamDefaultReader- Gets an instance of
ReadableStreamDefaultReaderand locks theReadableStreamto that reader instance. This method accepts an object argument indicating options. The only supported option ismode, which can be set tobyobto create aReadableStreamBYOBReader, as shown here:
- Gets an instance of
let reader = readable.getReader({ mode: 'byob' });
tee()
The ReadableStream API has a method tee() that will split the flow of data from the
ReadableStream into two separate ReadableStream instances.
In the standard definition of the ReadableStream API, the tee() method creates two
separate ReadableStream instances (called “branches”) that share a single Reader that
consumes the data from the original ReadableStream (let’s call it the “trunk”). When one
of the two branches uses the shared Reader to pull data from the trunk, that data is
used to fulfill the read request from the pulling branch, and a copy of the data is pushed
into a queue in the other branch. That copied data accumulates in memory until something
starts reading from it.
This spec defined behavior presents a problem for us in that it is possible for one branch to consume data at a far greater pace than the other, causing the slower branch to accumulate data in memory without any backpressure controls.
In our implementation, we have modified the tee() method implementation to avoid this
issue.
Each branch maintains it’s own data buffer. But instead of those buffers containing a copy of the data, they contain a collection of refcounted references to the data. The backpressure signaling to the trunk is based on the branch wait the most unconsumed data in its buffer.
+----------------+| pull algorithm |+----------------+|⊽ ..........................................................+---------------+ . +---------------------+ +-------------------+| enqueue(data) | ---> | push data to branch | ---> | has pending read? |+---------------+ . +---------------------+ +-------------------+| . no | yes || . +-------------------+ | +--------------+| . | add data to queue | <-----+ | fulfill read || . +-------------------+ +--------------+| ............................................................| . +---------------------+ +-------------------++--------> | push data to branch | ---> | has pending read? |. +---------------------+ +-------------------+. no | yes |. +-------------------+ | +--------------+. | add data to queue | <-----+ | fulfill read |. +-------------------+ +--------------+............................................................
Unfortunately, with this model, we cannot completely avoid the possibility of one branch
reading much slower than the other but we do prevent the memory pileup that would otherwise
occur so long as the underlying source of the ReadableStream is paying proper attention to
the backpressure signaling mechanisms.
PipeToOptions
preventClosebool- When
true, closure of the sourceReadableStreamwill not cause the destinationWritableStreamto be closed.
- When
preventAbortbool- When
true, errors in the sourceReadableStreamwill no longer abort the destinationWritableStream.pipeTowill return a rejected promise with the error from the source or any error that occurred while aborting the destination.
- When