Signatur
Beschreibung
WritableStream ist das Gegenstück zu ReadableStream: Statt Daten zu liefern, nimmt ein beschreibbarer Stream Daten in Stücken (Chunks) entgegen und gibt sie an eine Senke (underlying sink) weiter, etwa eine Datei, eine Netzwerkverbindung, einen Socket oder einfach ein Array im Speicher. Die Senke legst du über ein Objekt mit den Methoden start(), write(), close() und abort() fest.
Geschrieben wird nicht direkt auf den Stream, sondern über einen WritableStreamDefaultWriter, den du mit getWriter() erhältst. Der Writer sperrt den Stream (locked wird true), sodass nur ein Schreiber gleichzeitig aktiv ist. Über writer.ready und writer.desiredSize bekommst du den Gegendruck (backpressure) mit: Ist die interne Warteschlange voll, solltest du vor dem nächsten write() auf ready warten.
Typische Einsatzfälle sind readable.pipeTo(writable) zum Weiterleiten von Fetch-Antworten, die File System Access API (FileSystemWritableFileStream erbt von WritableStream), WebTransport, WebSockets-Streams sowie das Schreiben in TransformStream-Paare wie TextEncoderStream oder CompressionStream.
Wichtige Mitglieder: die Eigenschaft locked, die Methoden getWriter(), close() und abort(reason). Ein WritableStream ist außerdem ein übertragbares Objekt und kann per postMessage() an Worker weitergereicht werden.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $underlyingSink | object | {} | Definiert die Senke: start(controller) (Initialisierung, darf ein Promise liefern), write(chunk, controller) (verarbeitet einen Chunk, Promise signalisiert Abschluss), close(controller) (Abschluss nach allen Writes), abort(reason) (Abbruch). controller ist ein WritableStreamDefaultController mit error() und signal. |
| $queuingStrategy | object | CountQueuingStrategy | ByteLengthQueuingStrategy | { highWaterMark: 1 } | Steuert den Gegendruck: highWaterMark (Füllgrenze der Warteschlange) und optional size(chunk) zur Bemessung eines Chunks. |
Beispiele
Eigene Senke erstellen und per Writer beschreiben
const empfangen = [];
const stream = new WritableStream({
start() {
console.log('Senke bereit');
},
write(chunk) {
empfangen.push(chunk);
},
close() {
console.log('Fertig:', empfangen.join(' '));
},
abort(reason) {
console.error('Abgebrochen:', reason);
}
});
const writer = stream.getWriter();
const schreiben = async () => {
for (const wort of ['Hallo', 'Streams', 'API']) {
await writer.ready; // Gegendruck beachten
await writer.write(wort);
}
await writer.close();
};
schreiben();
Fetch-Antwort per pipeTo() verarbeiten
const zaehleBytes = async (url) => {
let bytes = 0;
const response = await fetch(url);
await response.body.pipeTo(new WritableStream({
write(chunk) {
bytes += chunk.byteLength; // chunk ist ein Uint8Array
}
}));
return bytes;
};
zaehleBytes('https://example.com/').then((n) => console.log(`${n} Bytes empfangen`));
Text über TextEncoderStream in eine Senke schreiben
const log = new WritableStream({
write(chunk) {
console.log('Bytes:', chunk);
}
});
const encoder = new TextEncoderStream();
const fertig = encoder.readable.pipeTo(log);
const writer = encoder.writable.getWriter();
await writer.write('Hi');
await writer.close();
await fertig;
console.log('gesperrt?', log.locked);
Gegendruck mit CountQueuingStrategy und Abbruch
const langsam = new WritableStream({
write: (chunk) => new Promise((r) => setTimeout(() => {
console.log('geschrieben', chunk);
r();
}, 100))
}, new CountQueuingStrategy({ highWaterMark: 2 }));
const writer = langsam.getWriter();
console.log('desiredSize:', writer.desiredSize);
writer.write(1);
writer.write(2);
console.log('desiredSize:', writer.desiredSize);
setTimeout(() => writer.abort('Benutzer hat abgebrochen'), 150);
// Wichtig · Fallstricke
- Sperre: Solange ein Writer existiert, ist der Stream gesperrt.
stream.close(),stream.abort()und ein zweitesgetWriter()werfen dann bzw. lehnen mitTypeErrorab. Mitwriter.releaseLock()gibst du die Sperre frei. - Gegendruck ignorieren:
write()ohneawait writer.readypuffert unbegrenzt im Speicher – bei großen Datenmengen ein Speicherproblem. - Fehlerbehandlung: Lehnt das Promise aus
write()der Senke ab, wechselt der Stream in den Fehlerzustand; alle folgenden Writes schlagen fehl. Promises auswriter.write()immer mitawaitoder.catch()behandeln, sonst drohen unhandled rejections. abort()verwirft noch wartende Chunks;close()wartet dagegen, bis alle geschriebenen Chunks verarbeitet sind.- Kompatibilität: In allen aktuellen Browsern verfügbar; Firefox unterstützt
WritableStreamerst seit Version 100.controller.signalist neuer (ca. 2022). In Node.js global ab Version 18, davor überrequire('node:stream/web'). Die Übertragbarkeit perpostMessageist in Safari erst spät hinzugekommen.