fkm blog

software開発に関することを書いていきます

JavaScriptのGenerator関数

JavaScriptには処理を途中まで実行し、値をreturnすることのできるGenerator関数を定義することができる。

これがあると便利なのは、ゲームでキャラクターを動かす時。例えば「右に10フレーム移動して下に5フレーム動く」という動きはこんな感じで書ける。

class Point {
  constructor(x, y) {
    this.x = x;
    this.y = y;
  }
}
// Generator関数はfunction* で定義する
// アロー(=>)では定義できない(たぶん
function* move(p) {
    for (var i = 0 ; i < 10 ; i++) {
        ++p.x;
        yield p;
    }
    for (var i = 0 ; i < 5 ; i++) {
        ++p.y;
        yield p;
    }
}

使うときはこんな感じ。ゲームなので1フレーム分移動したら描画しないといけない点に注意。例としてconsole.log()で出してみる。

var p = new Point(3, 4);
var m = move(p);

// 100フレーム動かす
for (var i = 0 ; i < 100 ; i++) {
    let out = m.next();
    if (out.done) break;
    console.log(p);
}

Generator関数を呼んだ戻り値に対し、next()を呼ぶと、yieldが出てくるまで処理を実行してくれる。次にnext()を呼ぶと、yieldの続きから実行してくれる。関数実行が終わっていたら、next()の戻り値のdoneフィールドがtrueになってる。

もひとつ別な例。ずっと1を出力しつづけるGenerator関数を定義してみる。

function* get1() {
    while (true) yield 1;
}

これを10回呼んでみる。yieldの結果は、next()の戻り値のvalueフィールドに入っている。

var n = get1()
for (var i = 0 ; i < 10 ; i++) {
    console.log(n.next().value);
}

もちろんコンソールには1が10個表示される