Generator
任意の時点で処理を中断/再開できる関数
.next(hoge)や.throw(error)を実行することで、メインルーチンからコルーチン側にデータの送信ができる
これによって双方向通信ができることになる
function*を使ってジェネレータ関数を定義
yieldはその中でのみ使える
yieldに来ると処理を中断し、.next()でそこから続きが実行される
Generatorに対して.next()を呼び出すことで、前回の続きから処理を実行できる
例
code:ts
function* 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の部分が返されて停止する
だいぶ変わった動きをするmrsekut.icon
next()に引数を与えて実行すると、直前のyieldの返り値がこれになる ???
Generator関数内でreturnしてるやつはどのタイミングで実行される?
nextの引数
だいぶ不可解な動きをするmrsekut.icon
.next()を実行すると、前回の続きから、次のyieldまで実行されるが、
.next(v)を実行すると、開始時のyieldの左辺にvが渡される
code:ts
function* 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が非直感的すぎる気もするがmrsekut.icon
yieldの返り値(っぽいやつ)がanyになるのはこのため
終了の検知
.next()の返り値はIteratorResultなので.done()で終了判定ができる
code:ts
function* 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();
}
型定義
code:ts
interface 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>;
}
このIterableを消化し切るまでyieldを繰り返す
一つのyield*で複数のyieldを書いた感じになる
code:ts
function* generator() {
}
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*を使えばいい
code:ts
const 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メソッドの使いみち
Generatorの中で普通の関数呼び出してもちゃんと使える?w
特に処理の大きめの関数
参考
良い