Signatur
Beschreibung
Der Operator yield* ist nur innerhalb von Generatorfunktionen (function*) und asynchronen Generatorfunktionen (async function*) erlaubt. Er holt sich einen Iterator vom angegebenen Objekt und gibt jeden Wert dieses Iterators so aus, als hätte der äußere Generator ihn selbst mit yield geliefert. Damit lassen sich Generatoren aus kleineren Generatoren zusammensetzen, ohne jeden Wert per Schleife von Hand weiterzureichen.
Die Delegation ist vollständig: Werte, die per next(wert) an den äußeren Generator geschickt werden, kommen beim inneren Iterator an. Das gilt auch für throw() und return(). Sobald der innere Iterator fertig ist (done: true), liefert der Ausdruck yield* … dessen Rückgabewert, also den Wert aus der return-Anweisung des inneren Generators.
Als Operand eignet sich jedes iterierbare Objekt: Arrays, Strings, Map, Set, arguments, NodeLists oder andere Generatoren. In einem async function* nutzt yield* zuerst Symbol.asyncIterator. Fehlt diese Methode, weicht es auf Symbol.iterator aus. So kann man dort auch an asynchrone Datenquellen wie Streams oder paginierte APIs delegieren.
Typische Anwendungsfälle sind rekursive Durchläufe (Bäume, verschachtelte Arrays), das Verketten mehrerer Datenquellen und das Aufteilen komplexer Generator-Logik in wiederverwendbare Teilgeneratoren.
Parameter
| Name | Typ | Default | Beschreibung |
|---|---|---|---|
| $iterable Pflicht | Iterable | AsyncIterable | Ein iterierbares Objekt, also eines mit [Symbol.iterator]() bzw. in async-Generatoren [Symbol.asyncIterator](). Ist der Wert nicht iterierbar, wird ein TypeError geworfen. |
Rückgabewert
value des letzten Ergebnisses mit done: true. Bei Arrays, Strings usw. ist das undefined.Beispiele
An andere Generatoren und Arrays delegieren
function* zahlen() {
yield 1;
yield 2;
}
function* alles() {
yield 0;
yield* zahlen(); // Generator
yield* [3, 4]; // Array
yield* 'ab'; // String
}
console.log([...alles()]);
Rekursiver Baumdurchlauf
const baum = {
name: 'root',
kinder: [
{ name: 'a', kinder: [{ name: 'a1', kinder: [] }] },
{ name: 'b', kinder: [] }
]
};
function* durchlaufe(knoten, tiefe = 0) {
yield `${' '.repeat(tiefe)}${knoten.name}`;
for (const kind of knoten.kinder) {
yield* durchlaufe(kind, tiefe + 1);
}
}
for (const zeile of durchlaufe(baum)) console.log(zeile);
Rückgabewert des inneren Generators auswerten
function* frageNamen() {
const name = yield 'Wie heißt du?';
return name.toUpperCase();
}
function* dialog() {
const ergebnis = yield* frageNamen();
yield `Hallo, ${ergebnis}!`;
}
const it = dialog();
console.log(it.next().value); // Frage
console.log(it.next('anna').value); // Wert geht an den inneren Generator
Paginierte API in einem async-Generator
async function* seite(url) {
const res = await fetch(url);
const { items, next } = await res.json();
yield* items; // synchrones Array, auch in async erlaubt
return next;
}
async function* alleEintraege(start) {
let url = start;
while (url) {
url = yield* seite(url); // delegiert und erhält die nächste URL
}
}
for await (const eintrag of alleEintraege('/api/items?page=1')) {
console.log(eintrag);
}
// Wichtig · Fallstricke
- Nur in Generatoren: Außerhalb von
function*bzw.async function*istyield*einSyntaxError. In Callbacks wieforEach(() => …)funktioniert es ebenfalls nicht, weil Pfeilfunktionen keine Generatoren sein können. Verwende stattdessenfor…of. yieldvs.yield*:yield [1, 2]liefert das Array als einen Wert,yield* [1, 2]liefert zwei einzelne Werte.- Der Rückgabewert fehlt bei
for…ofund Spread: Derreturn-Wert des inneren Generators wird nur an denyield*-Ausdruck übergeben.for…of, Spread undArray.fromgeben ihn nicht aus. - Nicht iterierbare Werte wie
null, Zahlen oder normale Objekte führen zu einemTypeError. - Weitergabe von
return()undthrow(): Wird der äußere Generator vorzeitig beendet, etwa durchbreak, wird auchreturn()des inneren Iterators aufgerufen. Dessenfinally-Blöcke laufen also. Hat der innere Iterator keinethrow()-Methode, wird er geschlossen und einTypeErrorgeworfen. - Rekursionstiefe: Sehr tiefe rekursive Delegation erzeugt eine Kette von Generatoren. Jeder Wert läuft durch alle Ebenen, was bei sehr tiefen Strukturen Leistung kostet.
- Kompatibilität: Wird von allen modernen Browsern und von Node.js unterstützt. Die Variante in async-Generatoren gibt es ab ES2018.