JavaScript の関数内で yield
キーワードを使用すると、関数の呼び出し側でイテレート処理(ループ処理)が可能な関数を定義することができます。
値を生成する関数になるので、ジェネレーター関数 と呼ばれます。
yield
キーワードは ES2015 (ECMAScript 6) で導入されました。ジェネレーター関数の基本
下記は number
値を生成するジェネレーター関数の実装例です(TypeScript の型としては Generator
を使います)。
ジェネレーター関数を定義するときは、function
の代わりに function*
を使います。
ジェネレーター関数が生成する次の値を取得するには、next()
メソッドを使用します。
next()
メソッドは、value
プロパティと done
プロパティを持つ IteratorResult
を返します。
すべての値の生成が終わると(ジェネレーター関数が return
すると)、value
プロパティの値は undefined
になります。
const gen = counter();
console.log(gen.next().value); // 1
console.log(gen.next().value); // 2
console.log(gen.next().value); // 3
console.log(gen.next().value); // undefined
ジェネレーター関数内の処理は、yield
が実行されたタイミングで一時停止します。
呼び出し側で next()
メソッドが呼び出されたときに実行が再開されます。
ジェネレーターのループ処理
ジェネレーターを for-of
ループで処理すると、生成された値を最後まで取得することができます。
このとき、自動的に value
プロパティの値を取り出してくれるので、次のようなシンプルなコードで処理できます。
const gen = counter();
for (const num of gen) {
console.log(num);
}
非同期なジェネレーター関数
非同期に実行されるジェネレーター関数を定義することもできます。
戻り値の型は Generator
ではなく、AsyncGenerator
になります。
AsyncGenerator
の next()
メソッドを呼び出すと、次の値を取得するための Promise
オブジェクトが返されます。
下記はもう少し非同期っぽく、1 秒おきに yield
するようにした例です。
async function* stringGenerator(): AsyncGenerator<string> {
const strs = ['This', 'is', 'a', 'pen'];
for (const s of strs) {
// 1 秒おきに yield
yield await new Promise((resolve) => {
setTimeout(() => resolve(s), 1000);
});
}
}
const gen = stringGenerator();
for await (const s of gen) {
console.log(s);
}