非同期プロセス制御-7行コード学会coモジュール


まず、私のタイトル党(●ー●)を許してください.tjオオカミさんのcoモジュールのソースコードは200行以上で、明らかに私などの無意味なコードが何行でも書き換えられます.ただ、今は「7日間で○○言語を習得する」などの速効仙丹が好きです.私も似たような名前の「7行コード学会coモジュール」を作って、目を集めています.
引きずられないようにJJというコードを先に出してみんなを驚かせます.
function co(gen) {
    var it = gen();
    var ret = it.next();
    ret.value.then(function(res) {
        it.next(res);
    });
}
極悪のはんてん
フロントエンドエンジニアにとっては、非同期のフィードバックはもう慣れていません.ブラウザの様々なインタラクティブロジックはイベントのフィードバックによって実現され、フロントエンドのロジックはますます複雑になり、レプリカ関数がますます多くなります.同時に、nodejsの流行はjavascriptをバックエンドの複雑なシーンに応用させて、nodejsコードの中には常に層の入れ子が見られます.
以下は、非同期要求によりページデータを取得し、ページデータからユーザ情報を要求し、最後にユーザ情報に基づいてユーザの製品リストを要求する典型的な非同期シーンである.多すぎるコールバック関数がネストされていて、プログラムのメンテナンスが難しくなり、悪のコールバックに発展しました.
$.get('/api/data', function(data) {
    console.log(data);
    $.get('/api/user', function(user) {
        console.log(user);
        $.get('/api/products', function(products) {
            console.log(products)
        });
    });
});
非同期フロー制御
  • の最も原始的な非同期プロセスの書き方は、上の例のようなコールバック関数のネスト法で、使ったことがある人なら誰でも知っています.
  • は後でPromiseが現れて、それは大いにコードの維持性を高めて、万悪の逆転ネストの問題を取り除いて、しかも今すでにES 6標準の一部になりました.
  • $.get('/api/data')
    .then(function(data) {
        console.log(data);
        return $.get('/api/user');
    })
    .then(function(user) {
        console.log(user);
        return $.get('/api/products');
    })
    .then(function(products) {
        console.log(products);
    });
  • の後、nodejsサークルにcoモジュールが現れました.ES 6のgeneratorとyieldに基づいて、同期の形で非同期コードを作成することができます.
  • co(function *() {
        var data = yield $.get('/api/data');
        console.log(data);
        var user = yield $.get('/api/user');
        console.log(user);
        var products = yield $.get('/api/products');
        console.log(products);
    });
  • 以上のPromiseとgeneratorが最初に創造した意図は、非同期プロセス制御を解決するためではない.Promiseは、「xxデータが準備されたら、thenがxxアクションを実行する」というプログラム思想のためのもので、非同期だけでなく、プログラムコードもPromiseを使用することができます.一方、GEneratorはES 6において、サブジェネレータであり、TJの創造性によって非同期プロセスを制御された.本当の非同期解決策はES 7のasyncを期待してください.ここでは主にcoモジュールについて説明します.
  • coモジュール
    上記で簡単に紹介したcoモジュールは、同期した形で非同期コードを作成できるnodejsモジュールであり、主にES 6のgeneratorのおかげです.nodejs>=0.11バージョンは--harmonyパラメータを加えてES 6のgenerator特性を体験することができます.iojsはデフォルトでgeneratorのサポートを開始しました.
    coを知るには、まず簡単にES 6のgeneratorとiteratorを知る必要があります.
    Iterator
    Iteratorディケンサは一つのオブジェクトで、一つのセットから一つのアイテムを取り出す方法を知っていますが、その現在のシーケンスの位置を追跡して、next()方法を提供して、シーケンスの次の項目に戻ります.
    var lang = { name: 'JavaScript', birthYear: 1995 };
    var it = Iterator(lang);
    var pair = it.next(); 
    console.log(pair); // ["name", "JavaScript"]
    pair = it.next(); 
    console.log(pair); // ["birthYear", 1995]
    pair = it.next(); // A StopIteration exception is thrown
    一見変わったところがないようですが、一歩ずつ相手の中のkeyとvalueを取るということではないですか?for ... inもできますが、それをゲナートと結び付けると大いに役に立ちます.
    Generator
    Generatorジェネレータは、自分の状態を保存できる簡単な関数を書くことによって反復アルゴリズムを定義することを可能にします.Generatorは停止して後に再び入ることができる関数です.ジェネレータの環境(バインディングされた変数)は実行毎に保存されますが、次回に入ると引き続き使用できます.generatorは文字通り「ジェネレータ」という意味で、ES 6ではサブジェネレータであり、一つのサブジェネレータのオブジェクトを生成するために使用されます.
    function *gen() {
        yield 'hello';
        yield 'world';
        return true;
    }
    上記のコードは簡単なgeneratorを定義しています.functionキーワードの後ろに*号があり、関数の中にyield文を使ってフロー制御ができます.
    var iter = gen();
    var a = iter.next();
    console.log(a); // {value:'hello', done:false}
    var b = iter.next();
    console.log(b); // {value:'world', done:false}
    var c = iter.next();
    console.log(c); // {value:true, done:true}
    gen()を実行すると、generator関数を実行するのではなく、1つのローズマリーを返します.シーケンサは、next()方法を有し、next()メソッドを呼び出すたびに、関数はyield文のところに実行される.next()メソッドはオブジェクトを返します.ここで、value属性はyieldキーワードの後の表式の値を表しています.done属性はオーバーロードしているかどうかを表しています.generatorジェネレータはnextyieldの協力によってフロー制御を実現し、上のコードは3回のnext()を実行して、generator関数体はやっと実行された.
    coモジュールの考え方
    上の例から、GEnerator関数は、次のnext()を実行するまで、yield文に止まることができます.coモジュールの考え方は、generatorのこの特性を利用して、非同期の操作をyieldの後に付け、非同期の操作が完了し、結果が戻ったら、次のnext()をトリガするということです.もちろん、yieldの後に付いている非同期の操作は一定の規範に従ってthunksとpromisesが必要です.
    yieldables
    The yieldable object s currently supported are:-promises-thunks-arrallel execution-Parallel exection-Parallel execution-generators-generantor funcions
    7行コード
    文章の先頭の7行のコードを見てください.
    function co(gen) {
        var it = gen();
        var ret = it.next();
        ret.value.then(function(res) {
            it.next(res);
        });
    }
    まず、次の重ね着を生成して、next()を実行します.得られたvalueはPromiseオブジェクトで、Promise.then()の中でnext()を実行します.もちろんこれは原理的なデモンストレーションで、多くのエラー処理と繰り返しnext()を呼び出すロジックが書かれていません.
    以下の簡単な比較を行う.従来の方法では、sayhelloは非同期関数であり、helloworldを実行すると、先に"world"を出力してから"hello"を出力する.
    function sayhello() {
        return Promise.resolve('hello').then(function(hello) {
            console.log(hello);
        });
    }
    function helloworld() {
        sayhello();
        console.log('world');
    }
    helloworld();
    出力
    > "world"
    > "hello"
    coの方式では、"hello"を先に出力してから"world"を出力します.
    function co(gen) {
        var it = gen();
        var ret = it.next();
        ret.value.then(function(res) {
            it.next(res);
        });
    }
    function sayhello() {
        return Promise.resolve('hello').then(function(hello) {
            console.log(hello);
        });
    }
    co(function *helloworld() {
        yield sayhello();
        console.log('world');
    });
    出力
    > "hello"
    > "world"
    逆ピラミッドを削除sayhello/sayworld/saybyeが3つの非同期関数であると仮定すると、本当のcoモジュールでこのように書くことができる.
    var co = require('co');
    co(function *() {
        yield sayhello();
        yield sayworld();
        yield saybye();
    });
    出力
    > "hello"
    > "world"
    > "bye"
    参照
    《es 7-async》https://github.com/jaydson/es7-async 『Generator関数の意味と使い方』http://www.ruanyifeng.com/blog/2015/04/generator.html 《Iterator》https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Object/Iterator