Signatur
Beschreibung
Ein TextEncoderStream ist ein Transform-Stream mit zwei Seiten. In die writable-Seite schreibt man Zeichenketten, an der readable-Seite kommen die passenden UTF-8-Bytes als Uint8Array-Blöcke heraus. Die Eigenschaft writable gibt einen WritableStream<string> zurück. Bei jedem Zugriff ist das dasselbe Objekt.
Meist greift man nicht direkt auf writable zu. Stattdessen übergibt man das ganze Objekt an ReadableStream.prototype.pipeThrough(), das intern in writable schreibt und readable weiterreicht. Direkt braucht man writable, wenn man Text selbst in den Stream schreiben will, etwa über writable.getWriter(). Ein Beispiel ist ein Streaming-Upload per fetch(), bei dem die Daten nach und nach erzeugt werden.
Die Codierung ist immer UTF-8. Anders als beim TextDecoderStream kann man keine andere Codierung wählen. Ungepaarte Ersatzzeichen (Surrogates) werden durch U+FFFD ersetzt. Ein hohes Surrogate am Ende eines Blocks wird bis zum nächsten Block zurückgehalten. So bleiben Emojis heil, auch wenn sie auf zwei Schreibaufrufe verteilt sind.
Rückgabewert
readable weitergereicht.Beispiele
Text direkt schreiben und Bytes lesen
const encoder = new TextEncoderStream();
const writer = encoder.writable.getWriter();
const reader = encoder.readable.getReader();
// Schreiben und Schließen, ohne auf den Leser zu blockieren
writer.write('Grüße ');
writer.write('👋');
writer.close();
const chunks = [];
while (true) {
const { value, done } = await reader.read();
if (done) break;
chunks.push(...value);
}
console.log(chunks.length, chunks.slice(0, 3));
Streaming-Upload mit fetch()
const encoder = new TextEncoderStream();
const writer = encoder.writable.getWriter();
// Upload startet sofort, Daten folgen nach und nach
const upload = fetch('/api/log', {
method: 'POST',
headers: { 'Content-Type': 'text/plain; charset=utf-8' },
body: encoder.readable,
duplex: 'half' // Pflicht bei Stream-Bodies
});
for (let i = 1; i <= 3; i++) {
await writer.write(`Zeile ${i}\n`);
await new Promise(r => setTimeout(r, 500));
}
await writer.close();
const res = await upload;
console.log(res.status);
Über pipeThrough (writable wird intern genutzt)
const textStream = new ReadableStream({
start(controller) {
controller.enqueue('Hallo, ');
controller.enqueue('Welt!');
controller.close();
}
});
const byteStream = textStream.pipeThrough(new TextEncoderStream());
const bytes = await new Response(byteStream).arrayBuffer();
console.log(bytes.byteLength);
// Wichtig · Fallstricke
- Nur lesbar: Eine Zuweisung an
writablewird ignoriert, im Strict Mode gibt es einenTypeError. - Sperre: Nach
getWriter()ist der Stream gesperrt (writable.locked === true).pipeThrough()oderpipeTo()schlagen dann fehl, bis der Writer mitreleaseLock()freigegeben wird. - Gegendruck (Backpressure): Wenn niemand von
readableliest, lösen sich die Promises vonwriter.write()ab dem Füllstand der Warteschlange nicht mehr auf. Wer jeden Aufruf mitawaitabwartet, ohne dass parallel gelesen wird, blockiert. - Werte, die keine Zeichenketten sind, werden mit
String()umgewandelt. Aus123wird also"123", und Objekte werden zu"[object Object]". - Streaming-Request-Bodies mit
fetch()brauchenduplex: 'half'und HTTP/2. Firefox und Safari unterstützen sie noch nicht. - In Node.js ist
TextEncoderStreamab Version 18 global verfügbar, davor überrequire('node:stream/web').