Signatur
Beschreibung
ReadableStreamBYOBRequest gehört zur Streams API und wird nur bei Byte-Streams verwendet, also bei einem ReadableStream, der mit type: "bytes" erzeugt wurde. BYOB steht für „Bring Your Own Buffer“: Der Verbraucher liest über einen ReadableStreamBYOBReader und übergibt dabei eigenen Speicher. Die Quelle schreibt ihre Daten direkt dort hinein. Das spart Zwischenkopien und Speicherzuweisungen, was bei großen Binärdaten wie Dateien, Netzwerkprotokollen oder Mediendaten spürbar hilft.
Du erzeugst keine Instanz selbst. Du bekommst sie in der pull()-Methode (oder in start()) der zugrundeliegenden Quelle über controller.byobRequest, wobei controller ein ReadableByteStreamController ist. Ist gerade keine Anforderung offen, ist der Wert null. Mit autoAllocateChunkSize legt der Stream auch für normale Default-Reader automatisch einen Puffer an. Dann gibt es immer eine byobRequest, und die Quelle braucht nur einen einzigen Codepfad.
Die Schnittstelle hat ein Attribut und zwei Methoden:
view: einArrayBufferView(in der Praxis einUint8Array), in das die Quelle schreiben soll, odernull, wenn die Anforderung schon erledigt ist.respond(bytesWritten): meldet, dassbytesWrittenBytes inviewgeschrieben wurden.respondWithNewView(view): übergibt stattdessen eine neue Ansicht auf denselben (übertragenen) Puffer, etwa wenn die Quelle den Puffer selbst übertragen hat.
Nach einem Aufruf von respond() oder respondWithNewView() ist die Anforderung erledigt. view wird null, und der Puffer geht an den Verbraucher zurück.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $bytesWritten Pflicht | number | Parameter von respond(): Anzahl der Bytes, die ab view.byteOffset in view geschrieben wurden. Im Zustand „readable“ muss der Wert größer als 0 und höchstens view.byteLength sein. Nach controller.close() muss er 0 sein. |
|
| $view Pflicht | ArrayBufferView | Parameter von respondWithNewView(): neue Ansicht auf denselben zugrundeliegenden Speicher. byteOffset muss dem von byobRequest.view entsprechen, und die Pufferlänge muss gleich bleiben. byteLength gibt die Zahl der geschriebenen Bytes an. |
Rückgabewert
respond() und respondWithNewView() geben undefined zurück. Das Attribut view liefert ArrayBufferView | null.Beispiele
Byte-Stream mit BYOB-Unterstützung (Zufallsdaten)
// Quelle, die direkt in den Puffer des Verbrauchers schreibt
let remaining = 64;
const stream = new ReadableStream({
type: 'bytes',
autoAllocateChunkSize: 16, // sorgt dafür, dass immer eine byobRequest existiert
pull(controller) {
const request = controller.byobRequest;
const view = request.view; // Uint8Array
const n = Math.min(view.byteLength, remaining);
crypto.getRandomValues(view.subarray(0, n));
remaining -= n;
request.respond(n);
if (remaining === 0) controller.close();
}
});
const reader = stream.getReader({ mode: 'byob' });
let buffer = new ArrayBuffer(32);
let total = 0;
while (true) {
const { value, done } = await reader.read(new Uint8Array(buffer));
if (done) break;
total += value.byteLength;
buffer = value.buffer; // der übertragene Puffer wird wiederverwendet
}
console.log('Gelesen:', total, 'Bytes');
Fallback: byobRequest oder enqueue()
// Ohne autoAllocateChunkSize kann byobRequest null sein (Default-Reader)
const data = new TextEncoder().encode('Hallo Streams!');
let offset = 0;
const stream = new ReadableStream({
type: 'bytes',
pull(controller) {
const request = controller.byobRequest;
if (request) {
const view = request.view;
const n = Math.min(view.byteLength, data.length - offset);
view.set(data.subarray(offset, offset + n));
offset += n;
request.respond(n);
} else {
controller.enqueue(data.slice(offset));
offset = data.length;
}
if (offset >= data.length) controller.close();
}
});
const reader = stream.getReader({ mode: 'byob' });
const { value } = await reader.read(new Uint8Array(5));
console.log(new TextDecoder().decode(value));
respondWithNewView() mit Teilansicht
const stream = new ReadableStream({
type: 'bytes',
pull(controller) {
const { view } = controller.byobRequest;
view[0] = 0xCA;
view[1] = 0xFE;
// neue Ansicht: gleicher Puffer und Offset, aber nur 2 Bytes geschrieben
const filled = new Uint8Array(view.buffer, view.byteOffset, 2);
controller.byobRequest.respondWithNewView(filled);
}
});
const reader = stream.getReader({ mode: 'byob' });
const { value } = await reader.read(new Uint8Array(8));
console.log([...value].map(b => b.toString(16)));
// Wichtig · Fallstricke
- Nur bei Byte-Streams:
controller.byobRequestgibt es nur mittype: "bytes". Bei normalen Streams hat der Controller dieses Attribut nicht. nullprüfen: Liest der Verbraucher mit einem Default-Reader und ist keinautoAllocateChunkSizegesetzt, istbyobRequestnull. Dann Daten mitcontroller.enqueue()einreihen.- Puffer werden übertragen: Der an
reader.read(view)übergebene Puffer wird abgetrennt (transferiert). Danach nur noch übervalue.bufferdarauf zugreifen, nicht über die alte Referenz. - Genau einmal antworten: Nach
respond()istviewnull. Ein zweiter Aufruf wirft einenTypeError. Auch eincontroller.enqueue()erledigt die offene Anforderung. - Nach
close(): Eine noch offene Anforderung muss mitrespond(0)beantwortet werden. Andere Werte lösen einenTypeErroraus. Umgekehrt gilt: Im offenen Zustand wirftrespond(0)einen Fehler. - Kompatibilität: Chrome und Edge ab 89, Firefox ab 102, Safari erst seit 18.4. Für ältere Safari-Versionen die Funktion vorher prüfen, etwa mit
typeof ReadableStreamBYOBRequest !== "undefined". In Node.js überstream/weboder global verfügbar.