Signatur
Beschreibung
Ein TransformStream hat zwei Enden: die writable-Seite, in die du Rohdaten schreibst, und die readable-Seite, aus der die Ergebnisse der transform()-Funktion herauskommen. Die Eigenschaft readable liefert dieses Ausgabe-Ende als ReadableStream.
Praktisch nutzt du readable meist indirekt über ReadableStream.prototype.pipeThrough(): Diese Methode erwartet ein Objekt der Form { writable, readable }, leitet den Quell-Stream in writable und gibt readable zurück. Direkt greifst du auf readable zu, wenn du den Stream z. B. als Response-Body weiterreichst, mit getReader() manuell ausliest, per for await...of iterierst oder an einen Worker überträgst.
Die Eigenschaft ist schreibgeschützt und liefert bei jedem Zugriff dasselbe Objekt. Der zurückgegebene Stream unterliegt den üblichen Regeln: Er kann nur von einem Leser gleichzeitig gesperrt werden (locked), und der Rückstau (Backpressure) wird automatisch an die writable-Seite weitergegeben.
Rückgabewert
controller.enqueue() ausgibt.Beispiele
Text in Großbuchstaben umwandeln und manuell auslesen
const upper = new TransformStream({
transform(chunk, controller) {
controller.enqueue(chunk.toUpperCase());
}
});
const writer = upper.writable.getWriter();
const reader = upper.readable.getReader();
// Schreiben und Lesen parallel, sonst blockiert der Rückstau
const schreiben = (async () => {
await writer.write('hallo ');
await writer.write('welt');
await writer.close();
})();
let ergebnis = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
ergebnis += value;
}
await schreiben;
console.log(ergebnis);
Verwendung mit pipeThrough() und fetch
const response = await fetch('/daten.txt');
// pipeThrough() gibt intern transformStream.readable zurück
const textStream = response.body.pipeThrough(new TextDecoderStream());
for await (const chunk of textStream) {
console.log('Empfangen:', chunk.length, 'Zeichen');
}
readable als Response-Body weiterreichen (Service Worker)
self.addEventListener('fetch', (event) => {
const { readable, writable } = new TransformStream();
// Antwort sofort mit der readable-Seite starten
event.respondWith(new Response(readable, {
headers: { 'Content-Type': 'text/plain; charset=utf-8' }
}));
// Inhalte nachträglich in die writable-Seite streamen
event.waitUntil((async () => {
const writer = writable.getWriter();
const enc = new TextEncoder();
for (const teil of ['Teil 1\n', 'Teil 2\n']) {
await writer.write(enc.encode(teil));
}
await writer.close();
})());
});
// Wichtig · Fallstricke
- Rückstau: Wird aus
readablenicht gelesen, füllt sich die interne Warteschlange undawait writer.write()kann für immer hängen bleiben. Schreiben und Lesen immer nebenläufig organisieren. - Sperre: Nach
getReader(),pipeTo()oderpipeThrough()istreadable.locked === true; ein zweiter Leser wirft einenTypeError. Für mehrere Konsumentenreadable.tee()verwenden. - Abbruch:
readable.cancel()bricht auch diewritable-Seite mit einem Fehler ab – laufende Schreibvorgänge schlagen dann fehl. - Kompatibilität: In allen aktuellen Browsern verfügbar; asynchrone Iteration (
for await...of) überReadableStreamwird in Safari erst seit neueren Versionen unterstützt. Übertragung perpostMessage(transferable streams) ist nicht überall verfügbar. In Node.js global ab Version 18, davor überrequire('node:stream/web').