Generator
昔のメモで書いてることがおかしいのでwipにしとく
.next(hoge)
や .throw(error)
を実行することで、メインルーチンからコルーチン側にデータの送信ができる
これによって双方向通信ができることになる
function*
を使ってジェネレータ関数を定義
yield
はその中でのみ使える
yield
に来ると処理を中断し、 .next()
でそこから続きが実行される
Generatorに対して .next()
を呼び出すことで、前回の続きから処理を実行できる
例
tsfunction* generator() {
console.log("in fn 1");
yield 0;
console.log("in fn 2");
yield 1;
console.log("in fn 3");
}
const g = generator(); // g::Generator<0|1, void, unknown>
console.log("1");
g.next();
console.log("2");
g.next();
console.log("3");
// 出力
// $ ts-node src/index.ts
// 1
// in fn 1
// 2
// in fn 2
// 3
挙動
上の generator()
自体は、呼び出されたときには何もしない
.next()
をすると実行され、
yield
に来ると、一時停止する
一つの yield
自体は return
のようなものなので yield hoge
の hoge
の部分が返されて停止する
nextの引数
だいぶ不可解な動きをする

.next()
を実行すると、前回の続きから、次のyieldまで実行されるが、
.next(v)
を実行すると、開始時の yield
の左辺に v
が渡される
tsfunction* genFunc() {
const x = yield 1111; // ①
console.log("x is " + x);
const y = yield 2222;
console.log("y is " + y);
}
const gen = genFunc();
const result = gen.next();
gen.next(result.value + 200); // ここの引数が①の`x`に渡される
gen.next();
// x is 1311
// y is undefined
逆に言えば、 const x = yield 1111;
という式だけを見ただけでは、 x
に何が入るのかは何もわからない
syntaxが非直感的すぎる気もするが

yield
の返り値(っぽいやつ)がanyになるのはこのため
終了の検知
.next()
の返り値は IteratorResult
なので .done()
で終了判定ができる
tsfunction* genFunc() {
yield console.log(1);
yield console.log(2);
yield console.log(3);
}
const gen = genFunc();
while (true) {
const result = gen.next(); // result :: IteratorResult<void, void>
if (result.done) {
break;
}
gen.next();
}
型定義
tsinterface Generator<T = unknown, TReturn = any, TNext = unknown>
extends Iterator<T, TReturn, TNext> {
next(...args: [] | [TNext]): IteratorResult<T, TReturn>;
return(value: TReturn): IteratorResult<T, TReturn>;
throw(e: any): IteratorResult<T, TReturn>;
[Symbol.iterator](): Generator<T, TReturn, TNext>;
}
この Iterable
を消化し切るまで yield
を繰り返す
一つの yield*
で複数の yield
を書いた感じになる
tsfunction* generator() {
yield* [1, 3, 5];
}
var gen = generator();
console.log(gen.next()); // { value: 1, done: false }
console.log(gen.next()); // { value: 3, done: false }
console.log(gen.next()); // { value: 5, done: false }
console.log(gen.next()); // { value: undefined, done: true }
Generatorの入れ子
yield*
を使えばいい
tsconst f = function* () {
yield 1;
yield 2;
};
const g = function* () {
yield 10;
yield* f();
yield 11;
};
for (const gen of g()) {
console.log(gen);
}
return
と throw
メソッドの使いみち
参考
良い