Signatur
Beschreibung
Der Konstruktor new WritableStream() erzeugt einen beschreibbaren Stream der Streams API. Du legst über das Objekt underlyingSink fest, was mit jedem geschriebenen Chunk passieren soll. Das kann zum Beispiel sein: in eine Datei schreiben, über einen WebSocket senden, in einen Puffer sammeln oder im DOM anzeigen. Der Stream sorgt dabei selbst für Warteschlange, Reihenfolge und Fehlerweitergabe.
Geschrieben wird meist nicht direkt, sondern über einen WritableStreamDefaultWriter, den du mit stream.getWriter() bekommst, oder über readableStream.pipeTo(writable). Gibt deine write()-Methode ein Promise zurück, wartet der Stream, bis es erfüllt ist, bevor er den nächsten Chunk liefert. So entsteht der Gegendruck: writer.ready und writer.desiredSize zeigen dem Produzenten, wann er langsamer werden sollte.
Mit dem zweiten Argument queuingStrategy steuerst du, wie viele Daten gepuffert werden, bevor Gegendruck entsteht. Dafür eignen sich CountQueuingStrategy (nach Anzahl der Chunks) oder ByteLengthQueuingStrategy (nach Bytes).
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $underlyingSink | Object | {} | Objekt mit optionalen Methoden:
controller ist ein WritableStreamDefaultController mit error(e) und signal (AbortSignal). |
| $queuingStrategy | Object | { highWaterMark: 1 } | Strategie für die Warteschlange: highWaterMark (nicht negative Zahl, ab der Gegendruck entsteht) und optional size(chunk) (liefert die Größe eines Chunks). Alternativ eine Instanz von CountQueuingStrategy oder ByteLengthQueuingStrategy. |
Beispiele
Einfacher Stream, der Chunks sammelt
const chunks = [];
const sink = new WritableStream({
write(chunk) {
chunks.push(chunk);
},
close() {
console.log('Fertig:', chunks.join(' '));
},
abort(reason) {
console.error('Abgebrochen:', reason);
}
});
const writer = sink.getWriter();
await writer.write('Hallo');
await writer.write('Welt');
await writer.close();
Fetch-Antwort per pipeTo verarbeiten und Bytes zählen
let bytes = 0;
const counter = new WritableStream({
write(chunk) {
// chunk ist ein Uint8Array
bytes += chunk.byteLength;
},
close() {
console.log(`Empfangen: ${bytes} Bytes`);
}
});
const response = await fetch('https://example.com/');
await response.body.pipeTo(counter);
Gegendruck mit asynchroner Senke und highWaterMark
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const slowSink = new WritableStream(
{
async write(chunk) {
await sleep(100); // simuliert langsames Netzwerk
console.log('geschrieben:', chunk);
}
},
new CountQueuingStrategy({ highWaterMark: 2 })
);
const writer = slowSink.getWriter();
for (let i = 1; i <= 4; i++) {
await writer.ready; // wartet, wenn die Warteschlange voll ist
console.log('desiredSize:', writer.desiredSize);
writer.write(i);
}
await writer.close();
Text dekodieren und ins DOM schreiben
const output = document.querySelector('#log');
const domSink = new WritableStream({
write(text) {
output.append(document.createTextNode(text));
}
});
const res = await fetch('/api/log.txt');
await res.body
.pipeThrough(new TextDecoderStream())
.pipeTo(domSink);
// Wichtig · Fallstricke
- Nur ein Schreiber: Solange ein Writer über
getWriter()aktiv ist, ist der Stream gesperrt (locked === true). Mitwriter.releaseLock()gibst du ihn wieder frei.pipeTo()sperrt den Stream ebenfalls. - Gegendruck beachten: Wer
writer.write()ohneawait writer.readyin einer Schleife aufruft, füllt die Warteschlange unbegrenzt und kann viel Speicher verbrauchen. - Fehler: Wirft
write()einen Fehler oder gibt ein abgelehntes Promise zurück, geht der Stream dauerhaft in den Fehlerzustand. Alle folgenden Schreibversuche werden dann abgelehnt. Behandle abgelehnte Promises vonwrite()/close(), sonst erscheinen unbehandelte Ablehnungen. start()wird synchron im Konstruktor aufgerufen. Gibt es ein Promise zurück, wirdwrite()erst aufgerufen, nachdem es erfüllt ist.controller.signalist erst seit etwa 2022 verfügbar (Chrome 98, Firefox 100, Safari 16.4). In älteren Umgebungen vorher prüfen.- Ein ungültiger
highWaterMark(negativ oderNaN) führt zu einemRangeError. - In Node.js ist die Klasse ab Version 18 global verfügbar, davor über
require('node:stream/web').