TSのジェネレータ関数
概要
ジェネレータ関数をイテレータオブジェクトを一時的に取得できることができる関数
TSのおけるイテレータオブジェクトとは以下のInterfaceを実装したオブジェクト
code:ts
interface Iterator<T> {
next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>;
throw?(e?: any): IteratorResult<T>;
}
関数を実行するときにyiledを実行すると、関数が止まりその時点のイテレータオブジェクトを返す
code:sample.ts
const friends = [
'yamada',
'takahashi',
'tanaka',
]
// ジェネレータ関数
function* generator(array: string[]): Generator<string, void> {
for(const value of array) {
yield value
}
}
const iterator = generator(friends)
console.log(iterator.next())
// {
// "value": "yamada",
// "done": false
// }
console.log(iterator.next())
// {
// "value": "takahashi",
// "done": false
// }
console.log(iterator.next())
// {
// "value": "tanaka",
// "done": false
// }
console.log(iterator.next())
// {
// "value": undefined,
// "done": true ☝️最後の要素に到達するとtrueになる
// }
ジェネレータ関数を呼び出しても実行されない。
code:ts
function* infiniteSequence() {
var i = 0;
while(true) {
yield i++;
}
}
var iterator = infiniteSequence(); // このタイミングではジェネレータは実行されずに、ジェネレータオブジェクトを作るだけ
while (true) {
console.log(iterator.next()); // { value: xxxx, done: false } forever and ever
}
iterator.next(value)を指定することで、yieldの処理を通過する
code:ts
function* generator() {
var name = yield 'yamada';
console.log(name); // bar!
}
const iterator = generator();
// 通常通りのイテレータオブジェクトのメソッド呼び出し
const yamada = iterator.next();
// {
// "value": "yamada",
// "done": false
// }
// next関数にデータを渡すと、generator関数のyiledがスキップされ、console.logが呼び出される
const tanaka = iterator.next('tanaka');
// yieldは存在しないので、done: trueになる
// {
// "value": undefined,
// "done": true
// }
itarator.throw(error)でジェネレータ関数内部で例外をなげることができる
code:ts
function* generator() {
try {
yield 'foo';
} catch(err) {
console.log(err.message); // bar!  new Error('bar')が表示
}
}
var iterator = generator();
var foo = iterator.next();
var nextThing = iterator.throw(new Error('bar'));
メリット
通常関数が一度実装すると最後まで
大きなデータの遅延評価が可能。大量データを一度をメモリに載せることなく、実行できるのでメモリ効率がよくなる
デメリット
制御フローがみにくい
エラーハンドリング(next() / throw() / return())が複雑
参考
サバイバルTypeScript ジェネレーター
TypeScript Deep Dive ジェネレータ
TypeScript Deep Dive Iterator