Signatur
Beschreibung
Die schreibgeschützte Eigenschaft byobRequest gehört zum ReadableByteStreamController, also dem Controller eines Byte-Streams (new ReadableStream({ type: "bytes", ... })). Liest ein Konsument den Stream mit einem ReadableStreamBYOBReader („Bring Your Own Buffer“), stellt er einen eigenen Puffer bereit. Über controller.byobRequest kommt die Quelle direkt an diesen Puffer (byobRequest.view) und kann die Daten ohne Zwischenkopie hineinschreiben.
Typischerweise fragt man die Eigenschaft in der pull()-Methode der zugrunde liegenden Quelle ab. Gibt es eine Anfrage, schreibt man bis zu view.byteLength Bytes in view und bestätigt dann mit byobRequest.respond(bytesWritten). Ist sie null, stellt man die Daten wie gewohnt mit controller.enqueue() bereit.
Wenn die Quelle mit autoAllocateChunkSize angelegt wurde, erzeugt der Stream auch für normale Leser (getReader() ohne mode: "byob") automatisch einen Puffer dieser Größe. Dann ist byobRequest in pull() immer gesetzt, und die Quelle braucht nur einen einzigen Codepfad.
Rückgabewert
ReadableStreamBYOBRequest mit der Eigenschaft view (ein Uint8Array bzw. ArrayBufferView, in den geschrieben wird) sowie den Methoden respond() und respondWithNewView(). Gibt null zurück, wenn keine BYOB-Anfrage offen ist.Beispiele
Zufallsbytes direkt in den Puffer des Lesers schreiben
const stream = new ReadableStream({
type: "bytes",
pull(controller) {
const request = controller.byobRequest;
if (request) {
// Direkt in den vom Leser bereitgestellten Puffer schreiben
const view = request.view;
crypto.getRandomValues(view);
request.respond(view.byteLength);
} else {
// Rückfall für Standard-Leser
controller.enqueue(crypto.getRandomValues(new Uint8Array(16)));
}
}
});
const reader = stream.getReader({ mode: "byob" });
const { value } = await reader.read(new Uint8Array(8));
console.log(value.byteLength);
Mit autoAllocateChunkSize nur einen Codepfad nutzen
const data = new TextEncoder().encode("Hallo, Byte-Stream!");
let offset = 0;
const stream = new ReadableStream({
type: "bytes",
autoAllocateChunkSize: 4,
pull(controller) {
// Wegen autoAllocateChunkSize ist byobRequest hier nie null
const { view } = controller.byobRequest;
const chunk = data.subarray(offset, offset + view.byteLength);
view.set(chunk);
offset += chunk.byteLength;
controller.byobRequest.respond(chunk.byteLength);
if (offset >= data.byteLength) {
controller.close();
}
}
});
// Auch ein normaler Leser funktioniert
const reader = stream.getReader();
const parts = [];
for (;;) {
const { done, value } = await reader.read();
if (done) break;
parts.push(value.byteLength);
}
console.log(parts);
Prüfen, ob eine Anfrage offen ist
let ctrl;
const stream = new ReadableStream({
type: "bytes",
start(controller) {
ctrl = controller;
}
});
console.log(ctrl.byobRequest); // noch kein Lesevorgang
const reader = stream.getReader({ mode: "byob" });
const pending = reader.read(new Uint8Array(10));
console.log(ctrl.byobRequest.view.byteLength);
ctrl.byobRequest.view.set([1, 2, 3]);
ctrl.byobRequest.respond(3);
console.log((await pending).value);
// Wichtig · Fallstricke
- Nur bei Byte-Streams: Die Eigenschaft gibt es nur, wenn der Stream mit
type: "bytes"angelegt wurde. Bei normalen Streams ist der Controller einReadableStreamDefaultController, und der hat keinbyobRequest. - Immer beantworten: Nach dem Schreiben in
viewmussrespond(n)oderrespondWithNewView(view)aufgerufen werden. Sonst bleibt dasread()-Promise des Lesers offen. - Übertragene Puffer: Nach
respond()ist die Anfrage erledigt und der alteview-Puffer übertragen (detached). Später darauf zuzugreifen ist ein Fehler. Deshalb die Eigenschaft bei jedempull()neu abfragen und keine Referenz zwischenspeichern. - Nach dem Schließen: Ist der Stream geschlossen, muss
respond(0)aufgerufen werden, um eine offene Anfrage abzuschließen. Ein Wert größer als 0 löst dann einenTypeErroraus. - Kompatibilität: Safari unterstützt Byte-Streams und BYOB-Leser erst ab Version 16.4, Firefox ab Version 102. Bei älteren Browsern vorher prüfen, ob
ReadableStreamBYOBReaderexistiert (Feature-Erkennung).