JavaScript(ES6)のジェネレータを試してみる

初めまして、初投稿です!

株式会社あゆたに所属する、元岡(もとおか)と申します。

主に AndroidiOS 等のスマフォアプリの開発を行っているエンジニアです。

最近は PhoneGap 等の クロスプラットフォームツールNode.js を使った案件も行う事もあって、JavaScript を使う機会が増えています。

私自身は Webのフロントエンドの経験が浅く、またES6 (ECMAScript6)についても触れる機会がなかったので、これを機に勉強しようかと思います。

そこで今回は、ジェネレータ について学習していきます。

プログラムにおいてジェネレータは、数列の各要素の値などを次々と生成(ジェネレート)し他の手続きに渡す、という機能を持っている手続きだそうです。

値を渡す方法としては、コールバックのようにして他の手続きを呼ぶものもあれば、呼び出される度に次々と異なる値を返す関数もあります。 JavaScriptの場合はyield(イールド)を使って値を返すので後者方ですかね。

JavaScriptのジェネレータは、イテレータを強力にサポートするものだそうです。

・まずは試してみる

では、簡単なサンプルを書いてみます。 ジェネレータの場合は、functionの後ろに*(アスタリスク)をつけます。

var g = function*(){  
   console.log("1");
   yield;
   console.log("2");
   yield;
   console.log("3");
   yield;
};

では、生成して実行してみましょう。 生成したオブジェクトに対してnext()を呼ぶ事で順次に処理されます。

var a = g();  
a.next();  
# 1
a.next();  
# 2
a.next();  
# 3

※#はコンソールログです

先ほどの ジェネレータ はこう書いても同じ動作をします。

var g = function*(){  
   var log = "1";
   yield console.log(log);
   log = "2";
   yield console.log(log);
   log = "3";
   yield console.log(log);
};

なるほど、、こんな動きをするんですね。。

次にyield*を使ってみます。 yield*は別のジェネレータを呼ぶ事ができます。

ではやってみましょう。

var g1 = function*(){  
   yield console.log("2");
   yield console.log("3");
};
var g2 = function*(){  
   yield console.log("1");
   yield* g1();
   yield console.log("4");
};

var a = g2();  
a.next();  
# 1
a.next();  
# 2
a.next();  
# 3
a.next();  
# 4

ジェネレータを部品化して、組み合わせる事ができそうです。

・値を返してみる

今度はyieldを使って、値を返してみましょう。 値はvalueプロパティで受け取るそうです。 ちなみにdoneプロパティでジェネレータの処理が全て完了しているか確認する事もできます。

var g = function*(){  
   yield 1;
   yield 2;
   yield 3;
};

var a = g();  
console.log(a.next().value);  
# 1
console.log(a.next().value);  
# 2
console.log(a.next().value);  
# 3
console.log(a.next().done);  
# true

返す値は固定値にしましたが、それ以外に演算結果や関数の戻り値を返す事もできます。

先ほどyield*で別なジェネレータを呼び出しましたが、実際に渡されてるのはジェネレータから生成したイテレータオブジェクトになります。

今回はイテレータオブジェクトの一つである配列を渡してみます。

var g = function*(){  
   yield* [1, 2, 3];
}

var a = g();  
console.log(a.next().value);  
# 1
console.log(a.next().value);  
# 2
console.log(a.next().value);  
# 3
console.log(a.next().done);  
# true

配列だけでなく、MapやSet等のオブジェクトを使っても色々できそうです。

・イテレータ的なものを作ってみる

だいたいジェネレータがどんなものか何となく判ってきましたでしょうか。 ジェネレータイテレータを自由に作成できるものだと理解すればいいでしょうか。。。

さて、今までのサンプルだと全然イテレータぽくないのでしたので、今度はジェネレータを使ってイテレータ的なものを作ってみましょう。

var g = function*(max){  
   var min = 1;
   while(min <= max){
      yield min++;
   }
};

var it = g(3);  
for (var value of it) {  
   console.log(value);
}
# 1
# 2
# 3

だいぶイテレータぽいものに(?)なってきました。 このジェネレータをラップしたり、クロージャを使ったりすれば、さらに面白い事ができそうです。

・終わりに

いかがだったでしょうか。 最初は戸惑いましたが、なかなか面白い動作をしますね。

ジェネレータについて学習をしようとしたキッカケですが、このジェネレータを使った非同期処理の記述の仕方があるそうです。 その前にまずはジェネレータについて理解しようと、この記事を書きました。

これまで非同期処理のいわるゆるコールバック地獄を避けるために JQueryDeferredES6Promiseを使っていました。 (DeferredPromiseついては、別途記事にしたいと考えてます)

このジェネレータを使って、さらにモダンでエレガンスな書き方ができるのではと期待しています!

こちら方も内容がまとまり次第、記事にしたいと思います。

それでは、また!