Start · Sprachen · JavaScript · Referenz · ReadableStream.from()

ReadableStream.from()

Methode

Die statische Methode erzeugt aus einem synchronen oder asynchronen iterierbaren Objekt einen <code>ReadableStream</code>, der dessen Werte der Reihe nach als Chunks ausgibt.

seit JavaScript Firefox 117, Node.js 20.6+, Deno, Bun; Chrome/Edge Kategorie: webapi

Signatur

ReadableStream.from(anyIterable)

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

Typ
ReadableStream
Beschreibung
Ein neuer 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);
}
Hallo Stream Welt

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
Erster Eintrag: { id: 1, ... } Generator beendet, Ressourcen freigegeben

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());
{"id":1,"name":"Artikel 1"} {"id":2,"name":"Artikel 2"} {"id":3,"name":"Artikel 3"}

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);
1 2 3

// 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ür Response), wandeln Sie Strings mit TextEncoderStream um.
  • Ist das Argument nicht iterierbar (z. B. ein normales Objekt oder null), wird sofort ein TypeError geworfen.
  • 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(…), nicht new ReadableStream.from(…). Bei Unterklassen erzeugt sie trotzdem immer einen einfachen ReadableStream.
  • 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.