generated at
Generator
JavaScript, TypeScriptのcoroutine





昔のメモで書いてることがおかしいのでwipにしとく



Generator関数の返り値がGenerator
TypeScriptのIteratorを返す関数
.next(hoge) .throw(error) を実行することで、メインルーチンからコルーチン側にデータの送信ができる
これによって双方向通信ができることになる




function* を使ってジェネレータ関数を定義
yield はその中でのみ使える
yield に来ると処理を中断し、 .next() でそこから続きが実行される
Generatorを返す
Generatorに対して .next() を呼び出すことで、前回の続きから処理を実行できる



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 の部分が返されて停止する



nextの引数
だいぶ不可解な動きをするmrsekut
.next() を実行すると、前回の続きから、次のyieldまで実行されるが、
.next(v) を実行すると、開始時の yield の左辺に v が渡される
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
yield の返り値(っぽいやつ)がanyになるのはこのため


終了の検知
.next() の返り値は IteratorResult なので .done() で終了判定ができる
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(); }


型定義
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>; [Symbol.iterator](): Generator<T, TReturn, TNext>; }



引数にTypeScriptのIterableを取る
この Iterable を消化し切るまで yield を繰り返す
一つの yield* で複数の yield を書いた感じになる
ts
function* 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* を使えばいい
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 メソッドの使いみち




参考
良い