ES 6生成器を深く検討する

12308 ワード

ES 6生成器に慣れていない場合は、まずhttp://www.cnblogs.com/linda586586/p/4282359.htmlのコードを読んで実行してください.基礎を身につけたと感じたら、細部について深く検討します.
エラー処理
    ES 6生成器設計において最も強力なのは、外部反復制御が非同期であっても、ジェネレータ内部コードの意味が同期されていることである.
    簡単に使えるかもしれません.おなじみのエラー処理技術はtry-catchの仕組みです.
    たとえば:
function *foo() {

    try {

        var x = yield 3;

        console.log( "x: " + x ); // may never get here!

    }

    catch (err) {

        console.log( "Error: " + err );

    }

}
    関数がyield 3で一時停止しても、一時停止状態のいずれかの時間を維持します.もしジェネレータにエラーが発生したら、try-catchはそれをキャプチャします.通常の非同期法を用いて,コールバック関数のように処理してみた.
    しかし、このジェネレータにエラーのフィードバックはどれぐらい正確ですか?
var it = foo();



var res = it.next(); // { value:3, done:false }



// instead of resuming normally with another `next(..)` call,

// let's throw a wrench (an error) into the gears:

it.throw( "Oops!" ); // Error: Oops!
    ローズマリーで使われているもう一つの方法――throw()は、ジェネレータに対して投げ間違いがあり、ジェネレータyieldの一時停止位置にちょうど発生しているようです.try-catch文は期待通りにエラーをキャッチしました.
    注:ジェネレータにエラーが発生しましたが、try-catchが捕獲されていません.エラーは元に戻ります.だから:
function *foo() { }



var it = foo();

try {

    it.throw( "Oops!" );

}

catch (err) {

    console.log( "Error: " + err ); // Error: Oops!

}
      明らかに、逆方向のエラー処理も効果がありました.
function *foo() {

    var x = yield 3;

    var y = x.toUpperCase(); // could be a TypeError error!

    yield y;

}



var it = foo();



it.next(); // { value:3, done:false }



try {

    it.next( 42 ); // `42` won't have `toUpperCase()`

}

catch (err) {

    console.log( err ); // TypeError (from `toUpperCase()` call)

}
プロキシジェネレータ
     また、作成したいのは、ジェネレータ関数の内部から別のジェネレータを呼び出します.これは、生成器を一般的な方法で実装するだけでなく、実際には他のジェネレータエージェントに対して反復制御するという意味です.このようにするために、yieldキーワードの変形の一つを使用することができます.例:
function *foo() {

    yield 3;

    yield 4;

}



function *bar() {

    yield 1;

    yield 2;

    yield *foo(); // `yield *` delegates iteration control to `foo()`

    yield 5;

}



for (var v of bar()) {

    console.log( v );

}

// 1 2 3 4 5
    前のページで紹介したように、他の文章の中のyield*foo()に代わりました.発生していることをより正確に説明できると思います.    これはどのように働いているのか見てみましょう.yield 1とyield 2は直接に彼らの値をfor of循環のnext()に送って呼び出して、私達の了解と期待のようです.
    しかし、yield*が遭遇しました.私たちはもう一つのジェネレータに実用化されていることに気づきます.だから、もう一つのジェネレータを反復して代理します.
    yield*が*bar()から**foo()までエージェントを行う時、for-of循環のnext()呼び出しはコントロールfoo()ですが、yield 3とyield 4は彼らの値をfor-ofサイクルに送ります.
    *foo()が終了すると、制御は元のジェネレータに戻り、最後にyield 5を呼び出します.
    この例は簡略化して外向きのみにする.もちろん、for-ofサイクルを使わずに、手動でローズマリーのnext()を呼び出して、その情報を中に送ります.それらの情報は同じように望む方式で、yield*エージェントを通じて伝達されます.
function *foo() {

    var z = yield 3;

    var w = yield 4;

    console.log( "z: " + z + ", w: " + w );

}



function *bar() {

    var x = yield 1;

    var y = yield 2;

    yield *foo(); // `yield*` delegates iteration control to `foo()`

    var v = yield 5;

    console.log( "x: " + x + ", y: " + y + ", v: " + v );

}



var it = bar();



it.next();      // { value:1, done:false }

it.next( "X" ); // { value:2, done:false }

it.next( "Y" ); // { value:3, done:false }

it.next( "Z" ); // { value:4, done:false }

it.next( "W" ); // { value:5, done:false }

// z: Z, w: W



it.next( "V" ); // { value:undefined, done:true }

// x: X, y: Y, v: V
    私たちはここで一つのエージェントを示しただけでも、*foo()は他のジェネレータではない理由がありません.そしてもう一つ、これに類推します.
    もう一つのyieldがプレイできるアクションは、プロキシ生成器からreturn値を受信することである.
function *foo() { yield 2; yield 3; return "foo"; // return value back to `yield*` expression } function *bar() { yield 1; var v = yield *foo(); console.log( "v: " + v ); yield 4; } var it = bar(); it.next(); // { value:1, done:false } it.next(); // { value:2, done:false } it.next(); // { value:3, done:false } it.next(); // "v: foo" { value:4, done:false } it.next(); // { value:undefined, done:true }
    彼が終了するまで繰り返し制御(next()を代行し、foo()からの任意の戻り値をyield*式の結果として設定し、ローカル変数vに値を与えたことが見られます.
    yieldとyield*は面白い違いがあります.yield表現を使って、結果は後のnextに送られましたが、yield*を使って、それはその代理のreturn値だけから結果を受けます.
    両方の方向からエラー処理を行うこともできます.yield*エージェントを通じて:
function *foo() {

    try {

        yield 2;

    }

    catch (err) {

        console.log( "foo caught: " + err );

    }



    yield; // pause



    // now, throw another error

    throw "Oops!";

}



function *bar() {

    yield 1;

    try {

        yield *foo();

    }

    catch (err) {

        console.log( "bar caught: " + err );

    }

}



var it = bar();



it.next(); // { value:1, done:false }

it.next(); // { value:2, done:false }



it.throw( "Uh oh!" ); // will be caught inside `foo()`

// foo caught: Uh oh!



it.next(); // { value:undefined, done:true }  --> No error here!

// bar caught: Oops!
    throw(「Uh oh!」)は*foo()内でyield*エージェントを通してtry-catchに間違って投げられました.*foo()内のthrow「Oops!」を投げ出して**barに戻し、もう一つのtry-catchで捕獲しました.彼らの中の一つを捕まえられなかったら、ミスは期待通りに外に伝播し続けます.
締め括りをつける
    ジェネレータには同期処理機構があります.すなわち、yield宣言によりtry-catchエラー処理が使用できます.ジェネレータ・ディケンサにも、ジェネレータが一時停止した位置でスローするthrow()方法があります.もちろん、ジェネレータ内部のtry-catchによって捕捉されても良いです.
    yieldは、現在の生成器から別のプロキシ反復制御を可能にする.結果はyield*が二つの方向に伝達しています.情報でも間違いでもいいです.
    しかし、もう一つの基本的な問題が残っています.答えがありません.ジェネレータはどうやってコードを同期する方式で助けてくれますか?私たちが今見ているこの二つの文章は、ジェネレータ関数の同期ローズマリーです.
    鍵は、非同期タスクを起動するためのジェネレータを一時停止する仕組みを構築し、非同期タスクの最後に回復することである.このような非同期制御を生成する多くの方法を検討します.英文原文:http://davidwalsh.name/es6-generators-dive