function* 宣言
Baseline
Widely available
This feature is well established and works across many devices and browser versions. It’s been available across browsers since 2016年9月.
function* 宣言は、新しいジェネレーター関数を指定された名前へのバインドとして作成します。ジェネレーター関数は、脱出した後でそのコンテキスト(変数のバインド)を保存したまま再入することが可能です。
ジェネレーター関数は function* 式を使って定義することもできます。
試してみましょう
function* generator(i) {
yield i;
yield i + 10;
}
const gen = generator(10);
console.log(gen.next().value);
// 予想される結果: 10
console.log(gen.next().value);
// 予想される結果: 20
構文
function* name(param0) {
statements
}
function* name(param0, param1) {
statements
}
function* name(param0, param1, /* …, */ paramN) {
statements
}
メモ: ジェネレーター関数には、対応するアロー関数はありません。
メモ:
function と * は別々なトークンなので、ホワイトスペースまたは改行で区切ることが可能です。
引数
name-
関数名。
param省略可-
関数の形式上の引数の名前。引数の構文については、関数リファレンスを参照してください。
statements省略可-
関数の本体を構成する文。
解説
function* 宣言は GeneratorFunction オブジェクトを生成します。ジェネレーター関数が呼び出されるたびに、新しい Generator オブジェクトが返され、これはイテレータープロトコルに準拠します。ジェネレーター関数の実行は、ある場所で中断されます。初期状態では関数本体の先頭で中断されます。ジェネレーター関数は複数回呼び出して複数のジェネレーターを同時に生成できます。各ジェネレーターは、ジェネレーター関数の実行コンテキストを独自に保持し、独立してステップ実行できます。
ジェネレーターは双方向の制御フローをすることができます。制御フローはジェネレーター関数(呼び出し先)とその呼び出し側の間で、双方が望む回数だけ移行できます。制御フローは呼び出し側から呼び出し先へ、ジェネレーターのメソッド、next()、throw()、return() を呼んで移行します。制御フローは、return や throw を使用して通常通り関数を終了させたり、すべての文を実行したり、yield および yield* 式を使用したりすることで、呼び出し側から呼び出し先へ進むことができます。
ジェネレーターの next() メソッドが呼び出されると、ジェネレーター関数の本体は次のいずれかになるまで実行されます。
yield式。この場合、next()メソッドは、yield で返された値を含むvalueプロパティと、常にfalseであるdoneプロパティを持つオブジェクトを返します。次にnext()が呼び出されると、yield式はnext()に渡された値に評価されます。- 別のイテレーターに委譲する
yield*演算子。この場合、ジェネレーターに対するこの呼び出しおよび以降のnext()呼び出しは、委譲先のイテレーターが完了するまで、委譲先のイテレーターに対するnext()呼び出しと同等となります。 return文(try...catch...finallyで介入されないもの)、または制御フローの終わり(暗黙的にreturn undefinedを意味します)。この場合、ジェネレーターは完了し、next()メソッドは返値を含むvalueプロパティと常にtrueとなるdoneプロパティを持つオブジェクトを返します。以降のnext()呼び出しは効果を持たず、常に{ value: undefined, done: true }を返します。- 関数内で発生したエラー(
throw文または未処理の例外による)。next()メソッドがこのエラーを発生させ、ジェネレータは完了する。以降のnext()呼び出しは効果なく、常に{ value: undefined, done: true }を返す。
ジェネレーターの throw() メソッドが呼び出されると、現在の中断位置でジェネレーターの本体に throw 文が挿入されたかのように動作します。同様に、ジェネレーターの return() メソッドが呼び出されると、現在の中断位置に return 文が挿入されたかのように動作します。どちらのメソッドも、ジェネレーター関数が try...catch...finally によって完了をキャッチしない限り、通常はジェネレーターを完了させます。
ジェネレーターはかつて非同期プログラミングのパラダイムであり、コールバック地獄を制御の反転によって避けることができました。現在では、この場合の解決はよりシンプルな async 関数モデルと Promise オブジェクトで解決されています。しかし、ジェネレーターは依然として他の多くのタスク、例えばイテレーターを直感的に定義するといった用途で有用です。
function* 宣言は function 宣言と同様の挙動を示します。これらはスコープの先頭に巻き上げられ、そのスコープ内のどこでも呼び出せます。また、特定のコンテキストでのみ再宣言が可能です。
例
>基本的な例
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
// …
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
ジェネレーターに引数を渡す
function* logGenerator() {
console.log(0);
console.log(1, yield);
console.log(2, yield);
console.log(3, yield);
}
const gen = logGenerator();
// 最初の next の呼び出しで、関数の最初から、
// 最初の yield 文の前まで実行される。
gen.next(); // 0
gen.next("pretzel"); // 1 pretzel
gen.next("california"); // 2 california
gen.next("mayonnaise"); // 3 mayonnaise
ジェネレーターにおける return 文
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 }
オブジェクトプロパティとしてのジェネレーター
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 }
オブジェクトメソッドとしてのジェネレーター
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 }
計算プロパティとしてのジェネレーター
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' ]
ジェネレーターはコンストラクターではない
function* f() {}
const obj = new f(); // throws "TypeError: f is not a constructor
ジェネレーターの例
function* powers(n) {
// 生成の無限ループ
for (let current = n; ; current *= n) {
yield current;
}
}
for (const power of powers(2)) {
// ジェネレーターを制御
if (power > 32) {
break;
}
console.log(power);
// 2
// 4
// 8
// 16
// 32
}
仕様書
| Specification |
|---|
| ECMAScript® 2026 Language Specification> # sec-generator-function-definitions> |
ブラウザーの互換性
関連情報
- 関数ガイド
- イテレーターとジェネレーターガイド
- 関数
GeneratorFunctionfunction*式functionasync functionasync function*- 反復処理プロトコル
yieldyield*Generator- Regenerator - GitHub
- Promises and Generators: control flow utopia presentation by Forbes Lindesay at JSConf (2013)
- Task.js on GitHub
- You Don't Know JS: Async & Performance, Ch.4: Generators by Kyle Simpson