0
0

Javascript ジェネレータにnext()で値を渡した際の挙動メモ

Posted at

ジェネレータ周りの実装を見ていた時に、next()で値を渡した際の挙動がわかりにくかったので、自分用にメモを残します

ジェネレータとは

定義については、Mozillaのドキュメントを見ていただければと思いますが、ジェネレータとは、
「ジェネレータ関数によって返されたもので、Arrayオブジェクトのようにイテラブルで、またイテレータでもある」
ものとなります。

今回はジェネレータに引数を渡す方法であるGaneratorオブジェクトのインスタンスメソッドnext()の引数を指定した場合の挙動を把握するためのコード例とその挙動を載せます。

結論: yieldのところに値が入る。初回の呼び出しでは値が渡せない

ジェネレータはyieldのところで停止し、next()メソッドで次のyieldのところまで進みます。
next()で渡した値は、yieldのところに入ります。
なので、yieldで変数の宣言・代入をしていると、値を渡すことができます。
ただし、書いている通り、初回の呼び出しでは値が渡されませんでした。

メモ1:変数の宣言、代入で指定すると、値を設定できる

下記のコードの例のように、yieldで変数valueの値を宣言すると、next()の引数を変数valueに渡すことができます。

注目したいのは、ジェネレータ関数の戻り値valuenullで、変数valueには引数が入っている点です。next()で値を渡さなかった場合は、変数valueundefinedになります。

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 }
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0