Signatur
Beschreibung
TextDecoderStream ist die Stream-Variante von TextDecoder aus der Encoding API. Das Objekt hat eine writable-Seite, die Byte-Blöcke (Uint8Array, ArrayBuffer oder DataView) annimmt, und eine readable-Seite, die dekodierte Zeichenketten liefert. Damit passt es direkt in ReadableStream.prototype.pipeThrough().
Am nützlichsten ist es beim Verarbeiten großer oder laufend eintreffender Textdaten, etwa bei einem fetch()-Antwortkörper, Server-Sent-Daten, NDJSON-Logs oder der Ausgabe von KI-Modellen, die Token für Token gestreamt wird. Du musst nicht auf die komplette Antwort warten (response.text()) und kannst jeden Block sofort weiterverarbeiten.
Der Stream kümmert sich selbst um Mehrbyte-Zeichen, die über Blockgrenzen hinweg zerteilt sind: Unvollständige Bytefolgen werden zurückgehalten, bis der nächste Block kommt. Bei einem Aufruf von TextDecoder.decode() pro Block würden solche Zeichen dagegen beschädigt, außer du übergibst { stream: true }.
Eigenschaften (alle schreibgeschützt): encoding (Name der Kodierung, z. B. "utf-8"), fatal, ignoreBOM, readable (ReadableStream<string>) und writable (WritableStream<BufferSource>).
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $label | string | "utf-8" | Bezeichnung der Kodierung, z. B. "utf-8", "iso-8859-1", "windows-1252", "utf-16le". Bei einer unbekannten Bezeichnung wird ein RangeError geworfen. |
| $options | object | {} | Optionales Einstellungsobjekt. |
| $options.fatal | boolean | false | Bei true bricht ungültige Byte-Eingabe den Stream mit einem TypeError ab. Bei false wird sie durch U+FFFD (�) ersetzt. |
| $options.ignoreBOM | boolean | false | Bei true bleibt eine Byte Order Mark (BOM) im Ausgabetext erhalten. Standardmäßig wird sie entfernt. |
Rückgabewert
readable und writable, die sich mit pipeThrough() einsetzen lässt.Beispiele
fetch-Antwort Stück für Stück als Text lesen
const response = await fetch('/api/grosse-datei.txt');
// Bytes → Zeichenketten
const textStream = response.body.pipeThrough(new TextDecoderStream());
let zeichen = 0;
for await (const chunk of textStream) {
zeichen += chunk.length;
console.log('Block empfangen:', chunk.slice(0, 40));
}
console.log(`Fertig, ${zeichen} Zeichen gelesen.`);
Instanz erzeugen und Eigenschaften prüfen
const decoder = new TextDecoderStream('windows-1252', { fatal: false });
console.log(decoder.encoding); // Name der Kodierung
console.log(decoder.fatal);
console.log(decoder.ignoreBOM);
console.log(decoder.readable instanceof ReadableStream);
console.log(decoder.writable instanceof WritableStream);
Zerteiltes Mehrbyte-Zeichen korrekt dekodieren
// „€“ ist in UTF-8 die Folge 0xE2 0x82 0xAC. Hier wird sie über zwei Blöcke verteilt.
const quelle = new ReadableStream({
start(controller) {
controller.enqueue(new Uint8Array([0x50, 0x72, 0x65, 0x69, 0x73, 0x3A, 0x20, 0xE2]));
controller.enqueue(new Uint8Array([0x82, 0xAC, 0x20, 0x35]));
controller.close();
}
});
const teile = [];
for await (const text of quelle.pipeThrough(new TextDecoderStream())) {
teile.push(text);
}
console.log(teile);
console.log(teile.join(''));
NDJSON-Stream zeilenweise parsen
// Hilfs-TransformStream: teilt Text an Zeilenumbrüchen auf
const zeilenSplitter = () => {
let rest = '';
return new TransformStream({
transform(chunk, controller) {
const zeilen = (rest + chunk).split('\n');
rest = zeilen.pop();
zeilen.filter(Boolean).forEach((z) => controller.enqueue(z));
},
flush(controller) {
if (rest) controller.enqueue(rest);
}
});
};
const res = await fetch('/api/events.ndjson');
const zeilen = res.body
.pipeThrough(new TextDecoderStream())
.pipeThrough(zeilenSplitter());
for await (const zeile of zeilen) {
const event = JSON.parse(zeile);
console.log(event.typ, event.id);
}
// Wichtig · Fallstricke
- Blockgrenzen passen nicht zu Zeilen oder JSON-Grenzen: Der Stream liefert Textstücke von beliebiger Länge. Wenn du Zeilen oder JSON-Objekte brauchst, schalte einen eigenen
TransformStreamdahinter (siehe NDJSON-Beispiel). - Asynchrone Iteration mit
for await...ofüberReadableStreamgibt es in Chrome 124+, Firefox 110+ und Node.js, in Safari aber erst ab 26.4. Für ältere Umgebungen liest du mitstream.getReader()und einer Schleife überreader.read(). - Mit
fatal: truebricht ungültiges UTF-8 den gesamten Stream mit einemTypeErrorab. Fang den Fehler also beim Lesen oder mitpipeTo().catch()ab. - Der Stream nimmt nur
BufferSourcean. Schreibst du Zeichenketten oder andere Werte inwritable, wird der Stream mit einemTypeErrorabgebrochen. - Die Gegenrichtung (Zeichenketten → UTF-8-Bytes) übernimmt
TextEncoderStream. Es kann nur nach UTF-8 kodieren. - Kompatibilität: In Firefox erst ab Version 105 verfügbar. Seit Node.js 18 ist das Interface global vorhanden, davor musst du es aus
node:stream/webimportieren.