Signatur
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
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);
}
})();
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 }
})();
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
})();
// 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*.