Signatur
Beschreibung
ReadableStream.from() macht aus jedem iterierbaren Objekt (Array, Set, Generator) oder asynchron iterierbaren Objekt (Async-Generator, anderer ReadableStream) einen neuen ReadableStream. Jeder Wert, den der Iterator liefert, wird zu einem Chunk im Stream. Ist der Iterator fertig, wird der Stream geschlossen.
Damit müssen Sie keinen eigenen new ReadableStream({ pull() { … } }) mit Controller-Logik mehr schreiben, wenn die Daten bereits als Iterator vorliegen. Das hilft vor allem, wenn Sie Async-Generatoren (etwa paginierte API-Abfragen) an Stream-basierte APIs übergeben wollen, zum Beispiel an new Response(stream), pipeThrough() mit TextEncoderStream/CompressionStream oder pipeTo().
Der Stream zieht die Werte nach Bedarf (Pull-Prinzip): Der Iterator wird erst weitergeschaltet, wenn ein Konsument liest. Wird der Stream abgebrochen (cancel()), ruft die Laufzeit return() am Iterator auf. Dadurch laufen finally-Blöcke in Generatoren und Ressourcen können freigegeben werden. Wirft der Iterator einen Fehler oder lehnt ein Promise ab, geht der Stream in den Fehlerzustand.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $anyIterable Pflicht | Iterable<any> | AsyncIterable<any> | Ein Objekt, das [Symbol.asyncIterator]() oder [Symbol.iterator]() implementiert. Der asynchrone Iterator wird bevorzugt. Liefert ein synchroner Iterator Promises, wird auf sie gewartet. |
Rückgabewert
ReadableStream ohne Byte-Modus. Er gibt die Werte des Iterators als Chunks aus und schließt sich, wenn der Iterator fertig ist.Beispiele
Stream aus einem Array erzeugen und lesen
const stream = ReadableStream.from(['Hallo', 'Stream', 'Welt']);
// ReadableStream ist selbst asynchron iterierbar
for await (const chunk of stream) {
console.log(chunk);
}
Paginierte API per Async-Generator als Stream
async function* ladeAlleSeiten(url) {
let seite = 1;
try {
while (true) {
const res = await fetch(`${url}?page=${seite}`);
const daten = await res.json();
if (daten.length === 0) return;
yield* daten;
seite++;
}
} finally {
console.log('Generator beendet, Ressourcen freigegeben');
}
}
const stream = ReadableStream.from(ladeAlleSeiten('https://api.example.com/items'));
const reader = stream.getReader();
const { value } = await reader.read();
console.log('Erster Eintrag:', value);
await reader.cancel(); // ruft return() am Generator auf -> finally läuft
NDJSON-Antwort aus einem Generator erzeugen (z. B. im Service Worker oder Node.js)
function* zeilen() {
for (let i = 1; i <= 3; i++) {
yield JSON.stringify({ id: i, name: `Artikel ${i}` }) + '\n';
}
}
const body = ReadableStream.from(zeilen())
.pipeThrough(new TextEncoderStream());
const response = new Response(body, {
headers: { 'Content-Type': 'application/x-ndjson' }
});
console.log(await response.text());
Fallback bei fehlender Unterstützung
const streamAus = (iterable) => {
if (typeof ReadableStream.from === 'function') {
return ReadableStream.from(iterable);
}
const it = (iterable[Symbol.asyncIterator] ?? iterable[Symbol.iterator]).call(iterable);
return new ReadableStream({
async pull(controller) {
const { value, done } = await it.next();
done ? controller.close() : controller.enqueue(value);
},
async cancel(reason) {
await it.return?.(reason);
}
});
};
for await (const n of streamAus([1, 2, 3])) console.log(n);
// Wichtig · Fallstricke
- Browser-Unterstützung: Firefox (ab 117), Node.js (ab 20.6), Deno und Bun unterstützen die Methode; Chromium-Browser erst in neueren Versionen, Safari noch nicht. Prüfen Sie im Browser vorher, ob
typeof ReadableStream.from === 'function'gilt, und verwenden Sie andernfalls ein Polyfill. - Der erzeugte Stream ist kein Byte-Stream.
getReader({ mode: 'byob' })funktioniert nicht. Wenn Sie Bytes brauchen (z. B. fürResponse), wandeln Sie Strings mitTextEncoderStreamum. - Ist das Argument nicht iterierbar (z. B. ein normales Objekt oder
null), wird sofort einTypeErrorgeworfen. - Strings sind iterierbar:
ReadableStream.from('abc')gibt einzelne Zeichen als Chunks aus, nicht den ganzen String. - Die Methode ist statisch. Der Aufruf lautet
ReadableStream.from(…), nichtnew ReadableStream.from(…). Bei Unterklassen erzeugt sie trotzdem immer einen einfachenReadableStream. - Einen unendlichen Generator können Sie gefahrlos übergeben. Er wird nur so weit ausgeführt, wie tatsächlich gelesen wird. Mit
cancel()beenden Sie ihn sauber.