Start · Sprachen · JavaScript · Referenz · yield*

yield*

Operator

Delegiert innerhalb einer Generatorfunktion an ein anderes iterierbares Objekt und gibt dessen Werte nacheinander aus. Das Ergebnis ist der Rückgabewert des inneren Iterators.

seit JavaScript ES2015 (in async-Generatoren: ES2018) Kategorie: syntax

Signatur

yield* iterable

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

Typ
any
Beschreibung
Den Rückgabewert des inneren Iterators, also 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()]);
[0, 1, 2, 3, 4, 'a', 'b']

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);
root a a1 b

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
Wie heißt du? Hallo, ANNA!

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* ist yield* ein SyntaxError. In Callbacks wie forEach(() => …) funktioniert es ebenfalls nicht, weil Pfeilfunktionen keine Generatoren sein können. Verwende stattdessen for…of.
  • yield vs. yield*: yield [1, 2] liefert das Array als einen Wert, yield* [1, 2] liefert zwei einzelne Werte.
  • Der Rückgabewert fehlt bei for…of und Spread: Der return-Wert des inneren Generators wird nur an den yield*-Ausdruck übergeben. for…of, Spread und Array.from geben ihn nicht aus.
  • Nicht iterierbare Werte wie null, Zahlen oder normale Objekte führen zu einem TypeError.
  • Weitergabe von return() und throw(): Wird der äußere Generator vorzeitig beendet, etwa durch break, wird auch return() des inneren Iterators aufgerufen. Dessen finally-Blöcke laufen also. Hat der innere Iterator keine throw()-Methode, wird er geschlossen und ein TypeError geworfen.
  • 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.