ユーザ用ツール

サイト用ツール


ecmascript:generator

Generator

Generator(ジェネレータ)はECMAScript 6で追加された新機能。

ジェネレータは、内部の状態管理が必要となるIterator(イテレータ)の反復処理において、オブジェクトよりも簡単・安全に状態管理を行うための機能である。

ジェネレータを用いた反復処理では「次の値の取り出し」と「取り出した値の処理」が交互に繰り返される。「次の値の取り出し」を行った後「取り出した値の処理」が完了するまでジェネレータ関数に記述された手続きの流れが一時中断される。

今までのJavaScriptでは実現できなかった手続き途中での中断という動作が非同期処理のより良いコーディング手法として注目されている。

ジェネレータの動作

function* f(i) {
  var y;
  y = yield ++i;
  console.log(y);        // => "a"
  y = yield ++i;
  console.log(y);        // => "b"
  y = yield ++i;
  console.log(y);        // => "c"
}
 
var g = f(1);               // g => [Object: generator]
console.log(g.next());      // => { value: 2, done: false }
console.log(g.next("a"));   // => { value: 3, done: false }
console.log(g.next("b"));   // => { value: 4, done: false }
console.log(g.next("c"));   // => { value: undefined, done: true }
  1. キーワード function* でジェネレータ関数を定義
  2. ジェネレータ関数を実行するとGeneratorオブジェクトが得られる
    • この時点ではジェネレータ関数の中身は実行されない
    • 引数 1 はジェネレータ関数に渡される
  3. Generator#next が呼ばれると、ジェネレータ関数の最初の yield 式まで実行される。
    • Generator#next が { value: 2, done: false } (valueはyield式の評価結果)を返す
  4. 再度 Generator#next が呼ばれる
    • 最初の yield が next の引数(“a”)を返す
    • ジェネレータ関数が次の yield 式まで実行される。
    • Generator#next が { value: 3, done: false } を返す。
  5. 再度 Generator#next が呼ばれる
    • 二番目の yield が next の引数(“b”)を返す
    • ジェネレータ関数が次の yield 式まで実行される。
    • Generator#next が { value: 4, done: false } を返す。
  6. さらに Generator#next が呼ばれる。
    • 三番目の yield が next の引数(“c”)を返す。
    • ジェネレータ関数の末尾まで実行される。
    • Generator#next が { value: undefined, done: true } を返す。

ジェネレータの例

フィボナッチ数列を生成するジェネレータ

function* fibonacci(limit = Infinity) {
    var count = 0;
    var a = 1;
    var b = 1;
    while(count++ < limit) {
        let c = a + b;
        a = b;
        b = c;
        yield c;        
    }
}
 
for (let i of fibonacci(5)) {
   console.log(i)
}

結果

2
3
5
8
13

Rangeを生成するジェネレータ

function* range(a, b) {
    for(var i = a; i <= b; i++) {
        yield i;
    }
}
 
for (let i of range(3,7)) {
   console.log(i)
}

結果

3
4
5
6
7

非同期処理

ジェネレータを用いた非同期処理

var g = (function* () {
   console.log(1);
   var x = yield setTimeout(function() { console.log(2); g.next(3); }, 1000);
   console.log(x);
   var y = yield setTimeout(function() { console.log(4); g.next(5); }, 2000);
   console.log(y);
})();
g.next();

結果

1  // ここで1秒待つ
2 
3  // ここで2秒待つ
4
5
ecmascript/generator.txt · 最終更新: 2015/10/24 04:29 by nullpon