Signatur
Beschreibung
Die schreibgeschützte Eigenschaft desiredSize berechnet sich aus highWaterMark minus der aktuellen Warteschlangengröße. Sie ist das zentrale Werkzeug für Gegendruck (engl. backpressure) in einer Push-Quelle: Eine Datenquelle, die von sich aus Daten liefert (z. B. WebSocket, Sensor, Timer), sollte pausieren, sobald der Wert 0 oder negativ wird, und fortfahren, wenn der Konsument wieder Platz geschaffen hat.
Wie die Größe gemessen wird, bestimmt die Queuing-Strategie im Konstruktor von ReadableStream: Bei CountQueuingStrategy (Standard, highWaterMark: 1) zählt jeder Chunk als 1, bei ByteLengthQueuingStrategy zählt die Byte-Länge der Chunks, bei einer eigenen size()-Funktion deren Rückgabewert.
Mögliche Werte:
- positive Zahl – so viel Platz ist noch frei; der Stream möchte mehr Daten.
0oder negativ – die Warteschlange ist voll bzw. überfüllt; die Quelle sollte pausieren.null– der Stream ist fehlerhaft (errored).0– auch, wenn der Stream bereits geschlossen ist.
Bei Pull-Quellen ist ein manuelles Prüfen meist unnötig, da der Stream pull() automatisch nur dann aufruft, wenn desiredSize > 0 ist.
Rückgabewert
highWaterMark minus aktuelle Größe). Kann negativ sein. null, wenn der Stream fehlerhaft ist; 0, wenn er geschlossen ist.Beispiele
Wert in Abhängigkeit von der Warteschlange beobachten
const stream = new ReadableStream(
{
start(controller) {
console.log('Start:', controller.desiredSize);
controller.enqueue('a');
console.log('Nach 1 Chunk:', controller.desiredSize);
controller.enqueue('b');
controller.enqueue('c');
console.log('Nach 3 Chunks:', controller.desiredSize);
controller.enqueue('d');
console.log('Überfüllt:', controller.desiredSize);
}
},
new CountQueuingStrategy({ highWaterMark: 3 })
);
Push-Quelle mit Gegendruck (Timer pausieren/fortsetzen)
let counter = 0;
let timer = null;
const stream = new ReadableStream(
{
start(controller) {
const produce = () => {
timer = setInterval(() => {
controller.enqueue(`Messwert ${++counter}`);
// Warteschlange voll -> Quelle pausieren
if (controller.desiredSize <= 0) {
clearInterval(timer);
timer = null;
}
}, 50);
};
controller.produceAgain = produce;
produce();
},
pull(controller) {
// Wird aufgerufen, wenn wieder Platz ist -> fortsetzen
if (!timer && counter < 10) controller.produceAgain();
if (counter >= 10) controller.close();
},
cancel() {
clearInterval(timer);
}
},
{ highWaterMark: 2 }
);
const reader = stream.getReader();
while (true) {
const { value, done } = await reader.read();
if (done) break;
console.log(value);
await new Promise((r) => setTimeout(r, 200)); // langsamer Konsument
}
Byte-basierte Strategie und Verhalten bei Fehler/Schließen
let ctrl;
const stream = new ReadableStream(
{ start(c) { ctrl = c; } },
new ByteLengthQueuingStrategy({ highWaterMark: 1024 })
);
ctrl.enqueue(new Uint8Array(300));
console.log(ctrl.desiredSize); // 1024 - 300
ctrl.close();
console.log(ctrl.desiredSize); // geschlossen
const s2 = new ReadableStream({ start(c) { c.error(new Error('kaputt')); ctrl = c; } });
console.log(ctrl.desiredSize); // fehlerhaft
// Wichtig · Fallstricke
- Negative Werte sind erlaubt:
enqueue()blockiert nie und wirft auch bei voller Warteschlange keinen Fehler. Gegendruck ist rein kooperativ – werdesiredSizeignoriert, riskiert unbegrenzt wachsenden Speicherverbrauch. nullprüfen: Ein Vergleich wiedesiredSize <= 0ergibt beinulltrue(danullzu0konvertiert wird) – das ist meist gewollt, aber für eine gezielte Fehlerbehandlung explizit=== nullprüfen.- Nach
close()ist der Wert0, obwohl noch ungelesene Chunks in der Warteschlange liegen können. - Bei Byte-Streams (
type: "bytes") erhält man stattdessen einenReadableByteStreamController, der eine gleichnamige Eigenschaft mit Byte-Semantik besitzt. - Kompatibilität: In allen aktuellen Browsern (Chrome, Edge, Firefox 65+, Safari) sowie in Node.js (global ab v18, über
node:stream/webab v16.5) und Deno verfügbar; auch in Web Workern.