Ich wollte den Umgang mit der Stream API besser beherrschen und habe daher mit String-Manipulation als Beispiel experimentiert. Dies implementiert einen TransformStream, der langen Text in Arrays einer bestimmten Größe aufteilt.
Implementierung
type Ctrl = TransformStreamDefaultController<string[]>;
class TextArrayTransformStream extends TransformStream<string, string[]> { #chunk: string[] = []; #chunkSize: number; #splitReg: RegExp;
constructor(chunkSize: number, maxTextLength: number) { super({ transform: (chunk, controller) => this.#handle(chunk, controller), flush: (controller) => this.#flush(controller), }); this.#chunkSize = chunkSize; this.#splitReg = new RegExp(`.{1,${maxTextLength}}`, "g"); }
#handle(chunk: string, controller: Ctrl): void { for (const str of chunk.match(this.#splitReg) || []) { if (this.#chunk.length >= this.#chunkSize) { controller.enqueue(this.#chunk); this.#chunk = []; } else { this.#chunk.push(str); } } }
#flush(controller: Ctrl): void { if (this.#chunk.length > 0) { controller.enqueue(this.#chunk); } }}Hilfsfunktion
function toReadableStream(text: string): ReadableStream<string> { return new ReadableStream({ start(controller) { controller.enqueue(text); controller.close(); } });}Verwendungsbeispiel
async function main() { const text = "Langer Text..."; const arrayLength = 5; // In Gruppen zu je 5 Elementen const textLength = 10; // Jedes Element hat 10 Zeichen
const stream = toReadableStream(text) .pipeThrough(new TextArrayTransformStream(arrayLength, textLength));
const reader = stream.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break;
console.log(value); // string[] wird sequentiell ausgegeben }}Anwendungsfälle
Dieses Muster ist in folgenden Szenarien effektiv:
- Beim Batch-Verarbeiten von LLM-API-Antworten
- Beim paginierten Anzeigen großer Textmengen
- Vorverarbeitung vor dem Senden an APIs mit Zeichenbegrenzung
Beachten Sie, dass AsyncIterator nicht implementiert ist, daher kann die for await…of-Syntax nicht verwendet werden.
Praxishinweis
Dieses Snippet passt gut, wenn dieselbe Operation oder Prüfung im Umfeld von TypeScript, JavaScript, Stream nicht immer wieder neu geschrieben werden soll. Als kleine Hilfsfunktion bleibt aufrufender Code leichter lesbar.
Wenn jedoch Verzweigungen und Voraussetzungen zunehmen, sollte nicht alles in ein einziges Snippet gepackt werden. Getrennte Schritte und klar abgegrenzte Helfer bleiben auf Dauer wartbarer.
hsb.horse