Signatur
Beschreibung
ReadableStreamBYOBReader ist neben ReadableStreamDefaultReader der zweite Lesertyp der Streams-API. „BYOB“ steht für Bring Your Own Buffer: Der Aufrufer übergibt bei jedem read() eine eigene ArrayBufferView (z. B. Uint8Array). Die Quelle schreibt die Bytes direkt in diesen Speicher. So werden Kopien und das Anlegen neuer Puffer vermieden.
Ein BYOB-Leser funktioniert nur mit Byte-Streams. Das sind Streams, die mit new ReadableStream({ type: "bytes", ... }) erzeugt wurden, oder z. B. Response.body bei fetch(). Man erhält den Leser über stream.getReader({ mode: "byob" }) oder mit new ReadableStreamBYOBReader(stream). Danach ist der Stream gesperrt, bis releaseLock() aufgerufen wird.
Wichtige Mitglieder:
constructor(stream): erzeugt einen Leser und sperrt den Stream.read(view, options?): gibt einPromise<{ value, done }>zurück.valueist eine neue View auf denselben Speicher mit den gelesenen Bytes. Ab Chrome 120 bzw. ES-Streams-Spezifikation gibt es die Option{ min }: Das Promise wird erst erfüllt, wenn mindestens so viele Elemente gelesen wurden.releaseLock(): gibt die Sperre auf den Stream frei.cancel(reason?): bricht den Stream ab.closed: Promise, das erfüllt wird, wenn der Stream geschlossen ist. Bei einem Fehler oder nach dem Freigeben der Sperre wird es abgelehnt.
Der Leser lohnt sich vor allem beim Parsen von Binärformaten, bei großen Datenmengen und wenn genau n Bytes in einen fest angelegten Puffer gelesen werden sollen, etwa für Dateiheader oder Netzwerkprotokolle.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $stream Pflicht | ReadableStream | Nur Konstruktor: der zu lesende Byte-Stream (type: "bytes"). Ist er kein Byte-Stream oder bereits gesperrt, wird ein TypeError geworfen. |
|
| $view Pflicht | ArrayBufferView | Für read(): Zielpuffer, z. B. Uint8Array oder DataView. Er muss eine Länge größer 0 haben. Der zugrunde liegende ArrayBuffer wird dabei übertragen (detached). |
|
| $options.min | number | 1 | Für read(): Mindestanzahl der Elemente, bevor das Promise erfüllt wird. Wird der Stream vorher geschlossen, kommen auch weniger Elemente zurück. |
| $reason | any | undefined | Für cancel(): Abbruchgrund, der an die Quelle weitergereicht wird. |
Rückgabewert
getReader({ mode: "byob" }) liefert den Leser. read() liefert Promise<{ value: ArrayBufferView | undefined, done: boolean }>.Beispiele
Leser erzeugen und einen Byte-Stream lesen
const stream = new ReadableStream({
type: "bytes",
start(controller) {
controller.enqueue(new Uint8Array([1, 2, 3, 4, 5]));
controller.close();
}
});
const reader = stream.getReader({ mode: "byob" });
// alternativ: new ReadableStreamBYOBReader(stream)
let buffer = new ArrayBuffer(8);
const { value, done } = await reader.read(new Uint8Array(buffer));
console.log(value, done);
console.log(buffer.byteLength); // alter Puffer ist übertragen
buffer = value.buffer; // Speicher weiterverwenden
Gesamten fetch-Body in einen wiederverwendeten Puffer lesen
const readAllBytes = async (url) => {
const response = await fetch(url);
const reader = response.body.getReader({ mode: "byob" });
let buffer = new ArrayBuffer(64 * 1024);
let total = 0;
try {
while (true) {
const { value, done } = await reader.read(new Uint8Array(buffer));
if (done) break;
total += value.byteLength;
// value hier verarbeiten ...
buffer = value.buffer; // Speicher für den nächsten Lesevorgang zurückholen
}
} finally {
reader.releaseLock();
}
return total;
};
console.log(`Gelesen: ${await readAllBytes("/daten.bin")} Bytes`);
Genau n Bytes lesen, z. B. einen Dateiheader
const readExactly = async (reader, n) => {
let buffer = new ArrayBuffer(n);
let offset = 0;
while (offset < n) {
const { value, done } = await reader.read(
new Uint8Array(buffer, offset, n - offset)
);
if (done) throw new Error("Stream endete vorzeitig");
buffer = value.buffer;
offset += value.byteLength;
}
return new Uint8Array(buffer);
};
const res = await fetch("/bild.png");
const reader = res.body.getReader({ mode: "byob" });
const header = await readExactly(reader, 8);
console.log([...header].map((b) => b.toString(16)).join(" "));
await reader.cancel("Header reicht");
// In neueren Umgebungen kürzer: await reader.read(new Uint8Array(8), { min: 8 })
// Wichtig · Fallstricke
- Übertragener Puffer: Nach
read(view)ist der ursprünglicheArrayBufferübertragen (byteLength === 0). Arbeite immer mitvaluebzw.value.bufferweiter. Ein Zugriff auf die alte View ergibt leere Daten. - Nur Byte-Streams:
getReader({ mode: \"byob\" })auf einem normalen Stream wirft einenTypeError. Mitview.byteLength === 0wird das Promise abgelehnt. - Gelesene Menge:
read()kann weniger Bytes liefern als der Puffer fasst. Prüfe deshalb immervalue.byteLengthoder nutze die Option{ min }. Diese ist neuer und wird noch nicht überall unterstützt. - Bei
done: trueistvalueeine leere View auf den Puffer oderundefined, wenn der Stream abgebrochen wurde. - Gibt es noch offene
read()-Aufrufe, führtreleaseLock()dazu, dass diese abgelehnt werden (nach aktueller Spezifikation). - Kompatibilität: Chrome/Edge ab 89 und Firefox ab 102. Safari unterstützt Byte-Streams und BYOB-Leser erst seit 18.4. In Node.js ist der Leser über
stream/webbzw. global verfügbar. Für ältere Umgebungen gibt es eine Feature-Erkennung mittypeof ReadableStreamBYOBReader !== \"undefined\"und als Ausweichlösung denReadableStreamDefaultReader.