W3docs

JavaScript Generator-Funktionen

JavaScript-Generatoren lernen: function*-Syntax, yield und yield*, Werte mit next(), return() und throw() senden, lazy infinite Sequenzen und Iterables.

Ein Generator ist eine besondere Art von Funktion, die sich mitten in der Ausführung anhalten kann, dem Aufrufer einen Wert zurückgibt und dann genau dort weitermacht, wo sie aufgehört hat, wenn das nächste Mal nach mehr gefragt wird. Anstatt alles im Voraus zu berechnen und einmal zurückzugeben, erzeugt ein Generator eine Folge von Werten träge — einen nach dem anderen, nur wenn angefordert.

Diese eine Idee macht Generatoren zum saubersten Weg in JavaScript, um benutzerdefinierte Iterables zu erstellen, unendliche Sequenzen ohne Speicherverschwendung abzubilden und langlaufende Logik auf Anfrage durchzugehen. Dieser Leitfaden behandelt die function*-Syntax, yield und yield*, die bidirektionale Kommunikation mit next(), das vorzeitige Beenden und die praktischen Muster, in denen Generatoren glänzen.

Diese Seite baut auf Iterables und Iteratoren auf — wenn das Iterator-Protokoll neu für Sie ist, lesen Sie das zuerst.

Generator-Funktionen verstehen

Die Grundlagen von Generator-Funktionen

Eine Generator-Funktion wird wie eine normale Funktion deklariert, aber mit einem Stern nach dem function-Schlüsselwort: function*. Der Stern ist das, was die Funktion als Generator kennzeichnet.

Der Aufruf einer Generator-Funktion führt ihren Rumpf nicht aus. Stattdessen gibt sie ein Generator-Objekt zurück — einen Iterator, den Sie manuell steuern. Jeder Aufruf seiner next()-Methode führt den Rumpf bis zum nächsten yield aus, hält dann an und gibt { value, done } zurück:

javascript— editable

Das erste next() läuft bis zum ersten yield 'Hello' und hält dort an. Das zweite setzt nach dieser Zeile fort und läuft bis yield 'World'. Das dritte setzt fort, findet keine weiteren yield-Anweisungen, erreicht das Ende der Funktion und meldet done: true. Weitere Aufrufe von next() würden weiterhin { value: undefined, done: true } zurückgeben.

Da ein Generator-Objekt auch iterierbar ist, können Sie es mit for...of, dem Spread-Operator oder Destructuring verwenden — all das hört automatisch auf, wenn done zu true wird:

javascript— editable
Hinweis

Ein Generator-Objekt kann nur einmal iteriert werden. Nachdem es erschöpft ist, liefert for...of nichts. Rufen Sie die Generator-Funktion erneut auf, um einen frischen Generator zu erhalten.

Der return-Wert eines Generators

Ein return innerhalb eines Generators beendet die Iteration und liefert den endgültigen value zusammen mit done: true. Beachten Sie, dass for...of diesen zurückgegebenen Wert ignoriert — es sieht nur die mit yield erzeugten:

javascript— editable

Den Ablauf mit return() und throw() steuern

Neben next() bietet ein Generator-Objekt zwei weitere Methoden, die es dem Aufrufer ermöglichen, die Ausführung von außen zu steuern:

  • generator.return(value) zwingt den Generator, sofort zu beenden, führt dabei alle finally-Blöcke aus und gibt { value, done: true } zurück.
  • generator.throw(error) injiziert eine Ausnahme am aktuellen yield-Punkt, sodass der Generator sie mit try...catch abfangen kann — oder sie weiterleitet, wenn er das nicht tut.
javascript— editable

Der finally-Block macht Generatoren zu einem ordentlichen Ort, um Ressourcen freizugeben (eine Datei schließen, einen Stream trennen), auch wenn die Iteration vorzeitig abgebrochen wird.

Fortgeschrittene Generator-Muster

Delegieren mit yield*

Wenn ein Generator die Werte eines anderen Generators oder eines beliebigen Iterables erzeugen muss, verwenden Sie yield* (ausgesprochen „yield-delegate") anstatt eine manuelle Schleife zu schreiben. Es leitet jeden Wert aus der delegierten Quelle transparent weiter:

javascript— editable

Ein nützliches Detail: Der Ausdruckswert von yield* ist der return-Wert des inneren Generators, was es ermöglicht, Generatoren zu kombinieren und ein Ergebnis an den äußeren zurückzugeben:

javascript— editable

Werte in Generatoren senden

Die Kommunikation mit einem Generator ist bidirektional. Ein yield-Ausdruck sendet nicht nur einen Wert nach außen über next(), er wird auch zu dem, was Sie beim nächsten next(value)-Aufruf hineingeben. Das verwandelt einen Generator in eine kleine interaktive Coroutine:

javascript— editable

Das erste next() läuft bis zum ersten yield und gibt die Frage zurück; sein Argument wird verworfen, weil noch kein pausiertes yield vorhanden ist, das es empfangen könnte. Das zweite next('Alice') setzt das pausierte yield fort, sodass der Ausdruck zu 'Alice' wird und name zugewiesen wird.

Praktische Anwendungen von JavaScript-Generatoren

Benutzerdefinierte Iterables erstellen

Die häufigste reale Anwendung von Generatoren ist es, eigene Objekte iterierbar zu machen. Definieren Sie [Symbol.iterator] als Generator-Methode, und jedes for...of, Spread oder Destructuring wird auf dem Objekt funktionieren. Das erfordert weit weniger Boilerplate als das Schreiben eines Iterator-Objekts mit einem handgestrickten next() (siehe Symbol-Typ für die Bedeutung von Symbol.iterator):

javascript— editable

Lazy und unendliche Sequenzen

Da ein Generator jeden Wert nur bei Bedarf berechnet, kann er eine konzeptuell unendliche Sequenz beschreiben, ohne jemals den Speicher zu erschöpfen. Das klassische Beispiel ist ein Zähler, aber derselbe Ansatz eignet sich für Bereiche, ID-Generatoren oder Fibonacci-Ströme. Man kombiniert ihn mit einem „take"-Helfer, der nach der benötigten Anzahl stoppt:

javascript— editable

Ein parametrisierter Bereichsgenerator ist eine praktische, wiederverwendbare Variante desselben Gedankens:

javascript— editable

Asynchrone Operationen verwalten

Historisch gesehen wurden Generatoren verwendet, um „Callback-Hell" zu glätten: Jedes yield pausierte bei einem Promise, und ein externer Runner setzte den Generator fort, sobald das Promise aufgelöst wurde. Das folgende Beispiel zeigt die manuelle Mechanik, damit Sie sehen können, was unter der Haube passiert:

javascript— editable
Hinweis

Dies ist ein vereinfachter manueller Runner. Im modernen Code würden Sie das mit async/await schreiben, das die Sprache direkt auf diesem Generator-plus-Promise-Muster aufgebaut hat. Für asynchrone Iteration (streaming von Chunks über die Zeit) verwenden Sie async generators mit async function* und for await...of.

Generatoren vs. reguläre Funktionen

AspektReguläre FunktionGenerator-Funktion
Deklarationfunction fn()function* fn()
Aufrufführt den Rumpf vollständig ausgibt ein Generator-Objekt zurück, führt noch nichts aus
Gibt zurückeinen einzelnen Werteinen Strom von Werten via yield
Kann pausieren?neinja, bei jedem yield
Iterierbar?neinja (funktioniert mit for...of, Spread)

Greifen Sie auf einen Generator zurück, wenn Sie Werte träge oder auf Anfrage produzieren müssen, ein Objekt iterierbar sein soll oder eine Sequenz potenziell unendlich ist. Für eine einfache einmalige Berechnung ist eine reguläre Funktion einfacher und schneller.

Fazit

Generatoren geben JavaScript eine saubere Möglichkeit, Sequenzen träge zu produzieren, Logik anzuhalten und fortzusetzen und jedes Objekt mit Schleifen wie for...of kompatibel zu machen. Beherrschen Sie yield zum Ausgeben von Werten, yield* zum Delegieren an andere Iterables und next()/return()/throw() zum Steuern und Kontrollieren des Ablaufs — und Sie haben ein Werkzeug, das alles antreibt, von benutzerdefinierten Iterables bis zur darauf aufbauenden async/await-Syntax.

Übungen

Übung
Welche Funktion hat das '*'-Zeichen vor dem Schlüsselwort function bei JavaScript-Generatoren?
Welche Funktion hat das '*'-Zeichen vor dem Schlüsselwort function bei JavaScript-Generatoren?
Was this page helpful?