ES6の取り扱いを開始したとき、ジェネレーターは、注目するイノベーションのリストのほぼ終わりに近づいています。 多くの場合、ジェネレーターをカスタムイテレーターを作成する簡単な方法と見なしますが、実際にははるかに興味深い機能を提供でき、ES6で最も興味深い革新の1つである可能性が高いです。
ジェネレーターとは何ですか?
ジェネレーターは、実行のさまざまな段階で開始、一時停止、再開できる機能です。 基本的に、この特別な関数は反復子を返します。 ジェネレーター関数は、
functionキーワードの後にアスタリスクで示され、特別な
yieldキーワードを使用するとすべての魔法が隠されます。
next()メソッドの次の呼び出し後にイテレータが返すものを決定します。
そしてすぐに、ES6の単純なジェネレーターの例:
function* myGenerator() { yield 'first'; let input = yield 'second'; yield input; }
ここで何が起こっているのでしょうか?
- 特別な構文関数* myGenerator(){}を使用して関数ジェネレーターを宣言します。
- この関数の最初の呼び出しは、反復子オブジェクトを返します。 このオブジェクトには、ジェネレータ関数を現在の状態で再開するnextメソッドがあります。
- ジェネレーター関数は、 iterator.nextを実行するまで実行を開始しません。
- iterator.nextが呼び出されるたびに、関数は最後の一時停止の場所から実行を再開し、次のyieldで「つまずき」、再び一時停止するまですべてのコードを実行します。
- iterator.next呼び出しは、 yieldに渡された値と、関数の実行が完了したかどうかを示すフラグを含むオブジェクトを返します。
イテレーターとしてのジェネレーター
記事の冒頭で、ジェネレーターはイテレーターを作成するための単純なメカニズムとしてよく使用されることが言及されました。 これはすべて
for構文を使用して実行できます。
例:
function* myGenerator() { yield 'first'; yield 'second'; yield 'third'; } for (var v of myGenerator()) { console.log(v); }
または例えば:
function* myGenerator(start, stop) { for (var i = start; i < stop; i++) yield i; } for (var v of myGenerator()) { console.log(v); }
さて、イテレータは素晴らしいですが、ジェネレータを使用して何ができるか見てみましょう。
そのため、何らかのバックエンドにログインし、認証トークンを使用してAPIからデータを抽出する必要があります。 ジェネレーターメカニズムを適用すると、次のようになります。
co(function* () { var result = yield login(username, password); var posts = yield getPosts(result.token); return posts; }).then(value => { console.log(value); }, err => { console.error(err); }); function login(username, password) { return fetch('/login', { method: 'post', body: JSON.stringify({ username: username password: password }) }).then(response => response.json()); } function getPosts(token) { return fetch('/posts', { headers: new Headers({ 'X-Security-Token': token }) }).then(response => response.json()); }
この例では
coライブラリを使用します。これにより、promiseメカニズムを使用して、非同期コードが同期的に見えるようになります。 ここでは、各
yieldについて、 promiseは結果をジェネレーター関数に返し、変数に保存します。
エクスプレスvsコア
えええ、コア? それは何ですか?Koaは(公式サイトのサブタイトルからの)
新世代のフレームワークで 、同じジェネレーターと
coライブラリを使用してミドルウェアの書き込みメカニズムを改善し、
コールバックからアプリケーションを
地獄から救います。
哲学的な観点から言えば、Koaは「ノードの修正と交換」を目指しており、Expressは「ノードの拡張」(Koaのドキュメントから)を目指しています。
Koaのミドルウェアは、ジェネレーターのメカニズムに基づいて構築され、「カスケード」を表します。 簡単に言えば、「ダウンストリーム」メカニズムはまずすべてのミドルウェアを介して開始され、次にすべてのミドルウェアを介して「アップストリーム」(
上流 )を開始します。
例:
var koa = require('koa'); var app = koa();
この例は、「Hello World」を返しますが、最初に:
- リクエストはミドルウェアを通過しますx-response-timeはyield次の行に行くすべてを行います。
- 次に、 yield nextは現在の機能を停止し、制御を別のミドルウェアロガーに転送し、 yield nextまですべてを実行します。
- ロガーでは、 yield nextは最後のミドルウェアを制御します。
- 彼は、順番に答えthis.body = 'Hello World'を生成します。
- ダウンストリーム送信用のミドルウェアはこれ以上ないため、 アップストリームプロセスが開始されます。 つまり 各ミドルウェアはyield nextの後に作業を再開し、アップストリームメカニズムを実現します。
一般的に言って、他に何がいいか。
Koaの詳細については、
公式Webサイトと
githubを参照してください 。
おわりに
ES6のジェネレーターは、よりクリーンで理解しやすい非同期コードを作成できる強力なメカニズムです。 コード全体で多数のコールバックを使用する代わりに、同期のように見える非同期コードを作成できるようになりましたが、実際には、ジェネレーターとキーワード
yieldの助けを借りて、非同期操作の完了を「待機」します。