The ReadableStreamBYOBReader
interface of the Streams API defines a reader for a ReadableStream
that supports zero-copy reading from an underlying byte source. It is used for efficient copying from underlying sources where the data is delivered as an "anonymous" sequence of bytes, such as files.
An instance of this reader type should usually be obtained by calling ReadableStream.getReader()
on the stream, specifying mode: "byob"
in the options parameter. The readable stream must have an underlying byte source. In other words, it must have been constructed specifying an underlying source with type: "bytes"
).
Using this kind of reader, a read()
request when the readable stream's internal queues are empty will result in a zero copy transfer from the underlying source (bypassing the stream's internal queues). If the internal queues are not empty, a read()
will satisfy the request from the buffered data.
Note that the methods and properties are similar to those for the default reader (ReadableStreamDefaultReader
). The read()
method differs in that it provide a view into which data should be written.
The example below is taken from the live examples in Using readable byte streams.
First create the reader using ReadableStream.getReader()
on the stream, specifying mode: "byob"
in the options parameter. As this is a "Bring Your Own Buffer" reader, we also need to create an ArrayBuffer
to read into.
const reader = stream.getReader({ mode: "byob" });
let buffer = new ArrayBuffer(200);
A function that uses the reader is shown below. This calls the read()
method recursively to read data into the buffer. The method takes a Uint8Array
typed array which is a view over the part of the original array buffer that has not yet been written. The parameters of the view are calculated from the data that was received in previous calls, which define an offset into the original array buffer.
readStream(reader);
function readStream(reader) {
let bytesReceived = 0;
let offset = 0;
reader
.read(new Uint8Array(buffer, offset, buffer.byteLength - offset))
.then(function processText({ done, value }) {
if (done) {
logConsumer(`readStream() complete. Total bytes: ${bytesReceived}`);
return;
}
buffer = value.buffer;
offset += value.byteLength;
bytesReceived += value.byteLength;
logConsumer(
`Read ${value.byteLength} (${bytesReceived}) bytes: ${value}`,
);
result += value;
return reader
.read(new Uint8Array(buffer, offset, buffer.byteLength - offset))
.then(processText);
});
}
When there is no more data in the stream, the read()
method resolves with an object with the property done
set to true
, and the function returns.
The ReadableStreamBYOBReader.closed
property returns a promise that can be used to monitor for the stream being closed or errored, or the reader lock being released.
reader.closed
.then(() => {
})
.catch(() => {
});
To cancel the stream call ReadableStreamBYOBReader.cancel()
, optionally specifying a reason. This returns a promise that will resolve when the stream has been cancelled. When the stream is cancelled the controller will in turn call cancel()
on the underlying source, passing in the optional reason.
The example code in Using readable byte streams calls the cancel method when a button is pressed, as shown:
button.addEventListener("click", () => {
reader.cancel("user choice").then(() => console.log("cancel complete"));
});
The consumer can also call releaseLock()
to release the reader's hold on the stream, but only when no read is pending: