ジェネレータ周りの実装を見ていた時に、next()
で値を渡した際の挙動がわかりにくかったので、自分用にメモを残します
ジェネレータとは
定義については、Mozillaのドキュメントを見ていただければと思いますが、ジェネレータとは、
「ジェネレータ関数によって返されたもので、Array
オブジェクトのようにイテラブルで、またイテレータでもある」
ものとなります。
今回はジェネレータに引数を渡す方法であるGaneratorオブジェクトのインスタンスメソッドnext()
の引数を指定した場合の挙動を把握するためのコード例とその挙動を載せます。
結論: yield
のところに値が入る。初回の呼び出しでは値が渡せない
ジェネレータはyield
のところで停止し、next()
メソッドで次のyield
のところまで進みます。
next()
で渡した値は、yield
のところに入ります。
なので、yield
で変数の宣言・代入をしていると、値を渡すことができます。
ただし、書いている通り、初回の呼び出しでは値が渡されませんでした。
メモ1:変数の宣言、代入で指定すると、値を設定できる
下記のコードの例のように、yield
で変数value
の値を宣言すると、next()
の引数を変数value
に渡すことができます。
注目したいのは、ジェネレータ関数の戻り値value
はnull
で、変数value
には引数が入っている点です。next()
で値を渡さなかった場合は、変数value
はundefined
になります。
function* gen() {
while (true) {
let value = yield null;
console.log(value);
}
}
const g = gen();
g.next(1);
// "{ value: null, done: false }"
g.next(2);
// 2
// "{ value: null, done: false }"
g.next();
// undefined
// "{ value: null, done: false }"
下記のように代入にしても同じ結果になります。
function* gen() {
let value = 5;
while (true) {
value = yield null;
console.log(value);
}
}
const g = gen();
g.next(1);
// "{ value: null, done: false }"
g.next(2);
// 2
// "{ value: null, done: false }"
g.next();
// undefined
// "{ value: null, done: false }"
メモ2:インクリメントしただけの場合は、値が設定されない
下記のコードの例のように、代入している場合は値を渡すことができます。
が、インクリメントの場合は、値が設定されませんでした。このあたりは同じ挙動です。
function* two() {
let index = 0;
while (true) {
index += yield index;
}
}
const g2 = two();
console.log(g2.next(1000)); // "{ value: 0, done: false }"
console.log(g2.next(1000)); // "{ value: 1000, done: false }"
console.log(g2.next()); // "{ value: NaN, done: false }"
function* three(){
let index = 0;
while (true)
yield index++;
}
const g3 = three();
console.log(g3.next(1000)); // "{ value: 0, done: false }"
console.log(g3.next(1000)); // "{ value: 1, done: false }"
console.log(g3.next()); // "{ value: 2, done: false }"
function* four(){
let index = 0;
while (true)
yield ++index;
}
const g4 = four();
console.log(g4.next(1000)); // "{ value: 1, done: false }"
console.log(g4.next(1000)); // "{ value: 2, done: false }"
console.log(g4.next()); // "{ value: 3, done: false }"
上記なので、インクリメントの場合でも、下記のようにすると渡すことはできます。
function* one() {
let value = 0;
while(true) {
value = yield ++value;
}
}
var g1 = one();
console.log(g1.next()); // "{ value: 1, done: false }""
console.log(g1.next(1000)); // "{ value: 1001, done: false }"
console.log(g1.next(1000)); // "{ value: 1001, done: false }"
console.log(g1.next()); // "{ value: NaN, done: false }"
メモ3: 初回のnext()
の呼び出しでは、値が渡されない
下記のコードの例のように、初回のnext()
の呼び出しで"zero0"
を渡していますが、その値は変数a
に設定されません。
正しく理解できているのか怪しいのですが、現時点の止まっているyield
のところに値を渡すものと思われ、初回のnext()
の呼び出しでは、値が渡せない仕様です。
function* gfn(){
let a = yield "first";
let b = yield "second";
let c = yield "three";
yield a + c;
}
var g = gfn();
console.log( g.next("zero0") ); // { value: "first", done: false }
console.log( g.next("first1") ); // { value: "second", done: false }
console.log( g.next("second2") ); // { value: "three", done: false }
console.log( g.next("three3") ); // { value: "first1three3", done: false }
console.log( g.next() ); // { value: undefined, done: true }