Signatur
Beschreibung
Ein TextDecoderStream ist ein Transform-Stream: In seine beschreibbare Seite (writable) werden Byte-Blöcke (Uint8Array, ArrayBuffer oder andere BufferSource) geschrieben, und die Eigenschaft readable stellt die zugehörige Ausgabeseite bereit. Aus diesem ReadableStream liest man die dekodierten Textstücke als string.
Typischerweise greift man nicht direkt auf readable zu, sondern übergibt den ganzen TextDecoderStream an ReadableStream.prototype.pipeThrough(). Das Ergebnis dieses Aufrufs ist genau dieser readable-Stream. Direkt verwenden muss man die Eigenschaft, wenn man Bytes manuell über writable.getWriter() einspeist oder die Ausgabe gezielt an andere Stellen weiterleitet (pipeTo(), tee()).
Wichtig: Die Chunk-Grenzen der Ausgabe entsprechen nicht Zeilen oder logischen Einheiten. Der Decoder puffert lediglich unvollständige Mehrbyte-Zeichen (z. B. UTF-8-Umlaute, die über zwei Byte-Blöcke verteilt sind) und gibt sie korrekt zusammengesetzt aus. Wer Zeilen braucht, muss selbst puffern und splitten.
Rückgabewert
string mit dekodiertem Text. Es wird bei jedem Zugriff dasselbe Objekt zurückgegeben.Beispiele
Fetch-Antwort als Text-Stream lesen
const response = await fetch('/api/log.txt');
// pipeThrough() liefert decoder.readable zurück
const textStream = response.body.pipeThrough(new TextDecoderStream());
let gesamt = '';
for await (const chunk of textStream) {
gesamt += chunk;
console.log(`Chunk mit ${chunk.length} Zeichen empfangen`);
}
console.log('Fertig:', gesamt.length, 'Zeichen');
Bytes manuell schreiben und über readable lesen
const decoder = new TextDecoderStream('utf-8');
const writer = decoder.writable.getWriter();
const reader = decoder.readable.getReader();
// „ü“ = 0xC3 0xBC – absichtlich auf zwei Chunks verteilt
writer.write(new Uint8Array([0x47, 0x72, 0xC3]));
writer.write(new Uint8Array([0xBC, 0xDF, 0x65]));
writer.close();
let text = '';
while (true) {
const { value, done } = await reader.read();
if (done) break;
text += value;
}
console.log(text);
Zeilenweise Verarbeitung eines Streams (z. B. NDJSON)
const zeilenSplitter = () => {
let rest = '';
return new TransformStream({
transform(chunk, controller) {
const teile = (rest + chunk).split('\n');
rest = teile.pop();
teile.forEach((z) => controller.enqueue(z));
},
flush(controller) {
if (rest) controller.enqueue(rest);
}
});
};
const response = await fetch('/api/events.ndjson');
const zeilen = response.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(zeilenSplitter());
for await (const zeile of zeilen) {
const event = JSON.parse(zeile);
console.log(event.type);
}
Ausgabe verzweigen mit tee()
const decoder = new TextDecoderStream();
const [fuerLog, fuerUI] = decoder.readable.tee();
const response = await fetch('/readme.txt');
response.body.pipeTo(decoder.writable);
const leseAlles = async (stream) => {
let s = '';
for await (const c of stream) s += c;
return s;
};
const [a, b] = await Promise.all([leseAlles(fuerLog), leseAlles(fuerUI)]);
console.log(a === b);
// Wichtig · Fallstricke
- Schreibgeschützt: Eine Zuweisung an
readablewird ignoriert (im Strict ModeTypeError). - Sperre: Nach
getReader()oderpipeTo()ist der Stream gesperrt (locked === true); ein zweiter Leser wirft einenTypeError. Mitreader.releaseLock()freigeben oder vorhertee()verwenden. - Chunk-Grenzen sind willkürlich: Ein Chunk kann mitten in einer Zeile oder einem JSON-Objekt enden – nie einzelne Chunks direkt parsen.
- Backpressure: Wird
readablenicht gelesen, füllt sich die interne Warteschlange undwriter.write()/writer.readybleiben hängen. Beim manuellen Schreiben nicht auf jedeswrite()warten, bevor gelesen wird, sonst droht ein Deadlock. - Fehlerhafte Bytes: Standardmäßig werden ungültige Sequenzen durch U+FFFD (�) ersetzt. Mit
new TextDecoderStream('utf-8', { fatal: true })gerätreadablestattdessen in den Fehlerzustand (TypeError). - Async-Iteration:
for await...ofüber einenReadableStreamwird in Safari erst ab Version 18.x (bzw. teils noch nicht) unterstützt; für breite KompatibilitätgetReader()mitread()-Schleife nutzen. - Kompatibilität: Firefox unterstützt
TextDecoderStreamerst ab Version 105. In Node.js ist die Klasse seit 16.6 global, vorher überrequire('node:stream/web').