Start · Sprachen · JavaScript · Referenz · AsyncGenerator

AsyncGenerator

Klasse

Ein <code>AsyncGenerator</code>-Objekt wird von einer <code>async function*</code> zurückgegeben und implementiert sowohl das asynchrone Iterator- als auch das asynchrone Iterable-Protokoll.

seit JavaScript ES2018 Kategorie: core

Signatur

class AsyncGenerator

Beschreibung

Ein AsyncGenerator ist das Ergebnis einer asynchronen Generatorfunktion (async function*). Er verhält sich wie ein normaler Generator, jedoch gibt jede Methode (next(), return(), throw()) ein Promise zurück, das sich zu einem { value, done }-Objekt auflöst. Dadurch können asynchrone Operationen – etwa API-Aufrufe, Stream-Verarbeitung oder zeitgesteuerte Ereignisse – als lazy, pull-basierte Sequenzen modelliert werden.

Weil AsyncGenerator das asynchrone Iterable-Protokoll implementiert (Symbol.asyncIterator), kann er direkt mit for await...of durchlaufen werden. Das macht ihn besonders nützlich für die Verarbeitung von Datenströmen, paginierten API-Ergebnissen oder Event-Sequenzen, ohne alle Werte vorab im Speicher halten zu müssen.

Der interne Zustand eines AsyncGenerator durchläuft vier Phasen: suspended start, executing, suspended yield und completed. Sobald der Generator in den Zustand completed übergeht (durch return, ein unbehandeltes throw oder das Ende der Funktion), liefern alle weiteren next()-Aufrufe { value: undefined, done: true }.

Wichtig: AsyncGenerator.prototype ist nicht direkt instantiierbar – Instanzen entstehen ausschließlich durch den Aufruf einer async function*. Die Klasse selbst ist nur über Object.getPrototypeOf(async function*(){}).prototype erreichbar.

Rückgabewert

Typ
AsyncGenerator
Beschreibung
AsyncGenerator-Instanzen entstehen ausschließlich beim Aufruf einer async function* und sind keine direkte Konstruktor-Rückgabe.

Beispiele

Einfacher asynchroner Generator mit for await...of

async function* countdown(start) {
  for (let i = start; i >= 0; i--) {
    await new Promise(resolve => setTimeout(resolve, 200));
    yield i;
  }
}

(async () => {
  for await (const value of countdown(3)) {
    console.log(value);
  }
})();
3 2 1 0

Paginierte API-Anfragen als AsyncGenerator

async function* fetchPages(baseUrl) {
  let page = 1;
  while (true) {
    const response = await fetch(`${baseUrl}?page=${page}`);
    if (!response.ok) return;
    const data = await response.json();
    if (!data.items?.length) return;
    yield* data.items;  // jeden Eintrag einzeln liefern
    if (!data.hasNextPage) return;
    page++;
  }
}

(async () => {
  const gen = fetchPages('https://api.example.com/entries');

  // Manueller Abruf der ersten drei Einträge
  const first = await gen.next();
  const second = await gen.next();
  const third = await gen.next();
  console.log(first.value, second.value, third.value);

  // Generator vorzeitig abbrechen (ruft finally-Block auf)
  await gen.return('fertig');
  const after = await gen.next();
  console.log(after); // { value: undefined, done: true }
})();
{ value: undefined, done: true }

Fehlerbehandlung mit throw()

async function* safeCounter() {
  let count = 0;
  while (true) {
    try {
      yield count++;
    } catch (err) {
      console.error('Fehler empfangen:', err.message);
      yield -1; // Sentinel-Wert nach Fehler
    }
  }
}

(async () => {
  const gen = safeCounter();
  console.log((await gen.next()).value);        // 0
  console.log((await gen.next()).value);        // 1
  console.log((await gen.throw(new Error('Ups!'))).value); // -1
  console.log((await gen.next()).value);        // 2
})();
0 1 Fehler empfangen: Ups! -1 2

// Wichtig · Fallstricke

Ressourcen-Freigabe: Wenn ein for await...of-Loop durch break, return oder eine Exception vorzeitig abbricht, ruft die Runtime automatisch gen.return() auf. Ein try...finally-Block im Generator stellt sicher, dass Ressourcen (z. B. offene Verbindungen) korrekt geschlossen werden.

Backpressure: Im Gegensatz zu Node.js-Streams bietet ein AsyncGenerator von Haus aus natürliches Backpressure-Verhalten: Der Produzent läuft erst weiter, wenn der Konsument next() erneut aufruft.

Browser-Kompatibilität: async function* und for await...of sind seit Chrome 63, Firefox 57, Safari 12 und Node.js 10 vollständig unterstützt. In älteren Umgebungen sind Babel-Transpilation und ein Regenerator-Runtime-Polyfill notwendig.

Kein direkter Konstruktor: new AsyncGenerator() ist nicht möglich und wirft einen TypeError. Instanzen entstehen ausschließlich durch Aufruf einer async function*.