function*
Baseline
Weitgehend verfügbar
Diese Funktion ist gut etabliert und funktioniert auf vielen Geräten und in vielen Browserversionen. Sie ist seit September 2016 browserübergreifend verfügbar.
Die function*-Deklaration erstellt eine Bindung einer neuen Generatorfunktion an einen bestimmten Namen. Eine Generatorfunktion kann verlassen und später wieder aufgenommen werden. Ihr Kontext (die Bindungen ihrer Variablen) bleibt dabei erhalten.
Sie können Generatorfunktionen auch mit einem function*-Ausdruck definieren.
Probieren Sie es aus
function* generator(i) {
yield i;
yield i + 10;
}
const gen = generator(10);
console.log(gen.next().value);
// Expected output: 10
console.log(gen.next().value);
// Expected output: 20
Syntax
function* name(param0) {
statements
}
function* name(param0, param1) {
statements
}
function* name(param0, param1, /* …, */ paramN) {
statements
}
Hinweis: Für Generatorfunktionen gibt es keine entsprechende Pfeilfunktionssyntax.
Hinweis:
function und * sind separate Token und können daher durch Leerzeichen oder Zeilenumbrüche getrennt werden.
Parameter
name-
Der Name der Funktion.
paramOptional-
Der Name eines formalen Parameters der Funktion. Informationen zur Syntax von Parametern finden Sie in der Referenz zu Funktionen.
statementsOptional-
Die Anweisungen, aus denen der Funktionskörper besteht.
Beschreibung
Eine function*-Deklaration erstellt ein GeneratorFunction-Objekt. Bei jedem Aufruf einer Generatorfunktion gibt sie ein neues Generator-Objekt zurück, das dem Iterator-Protokoll entspricht. Die Ausführung der Generatorfunktion ist an einer Stelle angehalten – anfangs ganz zu Beginn des Funktionskörpers. Die Generatorfunktion kann mehrfach aufgerufen werden, um mehrere Generatoren gleichzeitig zu erstellen. Jeder Generator verwaltet seinen eigenen Ausführungskontext der Generatorfunktion und kann unabhängig von den anderen schrittweise ausgeführt werden.
Der Generator ermöglicht einen Kontrollfluss in beide Richtungen: Der Kontrollfluss kann beliebig oft zwischen der Generatorfunktion (der aufgerufenen Funktion) und der aufrufenden Funktion wechseln. Von der aufrufenden Funktion zur Generatorfunktion wechselt er durch Aufrufe der Generatormethoden next(), throw() und return(). In die Gegenrichtung wechselt er, wenn die Funktion regulär durch return oder throw oder nach der Ausführung aller Anweisungen verlassen wird, oder durch Verwendung der Ausdrücke yield und yield*.
Wenn die Methode next() des Generators aufgerufen wird, wird der Körper der Generatorfunktion bis zu einem der folgenden Punkte ausgeführt:
- Ein
yield-Ausdruck. In diesem Fall gibt die Methodenext()ein Objekt zurück, dessen Eigenschaftvalueden mityieldbereitgestellten Wert enthält und dessen Eigenschaftdoneimmerfalseist. Beim nächsten Aufruf vonnext()wird deryield-Ausdruck zu dem Wert ausgewertet, der annext()übergeben wurde. - Ein
yield*-Ausdruck, der an einen anderen Iterator delegiert. In diesem Fall entsprechen dieser und alle weiteren Aufrufe vonnext()auf dem Generator Aufrufen vonnext()auf dem Iterator, an den delegiert wurde, bis dieser abgeschlossen ist. - Eine
return-Anweisung (die nicht vontry...catch...finallyabgefangen wird) oder das Ende des Kontrollflusses, das implizitreturn undefinedbedeutet. In diesem Fall ist der Generator abgeschlossen. Die Methodenext()gibt ein Objekt zurück, dessen Eigenschaftvalueden zurückgegebenen Wert enthält und dessen Eigenschaftdoneimmertrueist. Weitere Aufrufe vonnext()haben keine Wirkung und geben immer{ value: undefined, done: true }zurück. - Ein innerhalb der Funktion ausgelöster Fehler, entweder durch eine
throw-Anweisung oder durch eine nicht behandelte Ausnahme. Die Methodenext()löst diesen Fehler aus, und der Generator ist abgeschlossen. Weitere Aufrufe vonnext()haben keine Wirkung und geben immer{ value: undefined, done: true }zurück.
Wenn die Methode throw() des Generators aufgerufen wird, wirkt dies so, als würde an der aktuellen angehaltenen Position eine throw-Anweisung in den Körper des Generators eingefügt. Entsprechend wirkt ein Aufruf der Methode return() so, als würde dort eine return-Anweisung eingefügt. Beide Methoden schließen den Generator normalerweise ab, sofern die Generatorfunktion den jeweiligen Abschluss nicht mit try...catch...finally abfängt.
Generatoren wurden früher als Paradigma für die asynchrone Programmierung verwendet. Durch Inversion of Control ließ sich damit die sogenannte Callback Hell vermeiden. Heute wird dieser Anwendungsfall mit dem einfacheren Modell der asynchronen Funktionen und dem Promise-Objekt abgedeckt. Generatoren sind jedoch weiterhin für viele andere Aufgaben nützlich, etwa um Iteratoren auf unkomplizierte Weise zu definieren.
function*-Deklarationen verhalten sich ähnlich wie function-Deklarationen: Sie werden an den Anfang ihres Gültigkeitsbereichs gehoben und können überall innerhalb dieses Bereichs aufgerufen werden. Eine erneute Deklaration ist nur in bestimmten Kontexten möglich.
Beispiele
>Einfaches Beispiel
function* idMaker() {
let index = 0;
while (true) {
yield index++;
}
}
const gen = idMaker();
console.log(gen.next().value); // 0
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
// …
Beispiel mit yield*
function* anotherGenerator(i) {
yield i + 1;
yield i + 2;
yield i + 3;
}
function* generator(i) {
yield i;
yield* anotherGenerator(i);
yield i + 10;
}
const gen = generator(10);
console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20
Argumente an Generatoren übergeben
function* logGenerator() {
console.log(0);
console.log(1, yield);
console.log(2, yield);
console.log(3, yield);
}
const gen = logGenerator();
// the first call of next executes from the start of the function
// until the first yield statement
gen.next(); // 0
gen.next("pretzel"); // 1 pretzel
gen.next("california"); // 2 california
gen.next("mayonnaise"); // 3 mayonnaise
Return-Anweisung in einem Generator
function* yieldAndReturn() {
yield "Y";
return "R";
yield "unreachable";
}
const gen = yieldAndReturn();
console.log(gen.next()); // { value: "Y", done: false }
console.log(gen.next()); // { value: "R", done: true }
console.log(gen.next()); // { value: undefined, done: true }
Generator als Objekteigenschaft
const someObj = {
*generator() {
yield "a";
yield "b";
},
};
const gen = someObj.generator();
console.log(gen.next()); // { value: 'a', done: false }
console.log(gen.next()); // { value: 'b', done: false }
console.log(gen.next()); // { value: undefined, done: true }
Generator als Objektmethode
class Foo {
*generator() {
yield 1;
yield 2;
yield 3;
}
}
const f = new Foo();
const gen = f.generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 2, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: undefined, done: true }
Generator als berechnete Eigenschaft
class Foo {
*[Symbol.iterator]() {
yield 1;
yield 2;
}
}
const SomeObj = {
*[Symbol.iterator]() {
yield "a";
yield "b";
},
};
console.log(Array.from(new Foo())); // [ 1, 2 ]
console.log(Array.from(SomeObj)); // [ 'a', 'b' ]
Generatoren können nicht mit new aufgerufen werden
function* f() {}
const obj = new f(); // throws "TypeError: f is not a constructor
Generatorbeispiel
function* powers(n) {
// Endless loop to generate
for (let current = n; ; current *= n) {
yield current;
}
}
for (const power of powers(2)) {
// Controlling generator
if (power > 32) {
break;
}
console.log(power);
// 2
// 4
// 8
// 16
// 32
}
Spezifikationen
| Spezifikation |
|---|
| ECMAScript® 2027 Language Specification> # sec-generator-function-definitions> |
Browser-Kompatibilität
Siehe auch
- Leitfaden zu Funktionen
- Leitfaden zu Iteratoren und Generatoren
- Funktionen
GeneratorFunctionfunction*-Ausdruckfunctionasync functionasync function*- Iterationsprotokolle
yieldyield*Generator- Promises and Generators: control flow utopia, Vortrag von Forbes Lindesay auf der JSConf (2013)
- Task.js auf GitHub
- You Don't Know JS: Async & Performance, Ch.4: Generators von Kyle Simpson