非同期プログラミングスキーム:(非常に実用的なドロップ)
ジェネレータ関数は通常の関数であるが、2つの機能があります。まず、function
キーワードや関数名の間にアスタリスク(*)があり、第二に、使用する関数yield
の式を別の内部状態を定義するために(yield
英語での意味は、「出力」です)。
すべての呼び出しnext
方法、次回まで、ヘッドの機能や実行を停止するために一つの場所から内部ポインタyield
表現(あるいはreturn
これまでの文)。換言すれば、ジェネレータ関数は、セグメントを行うyield
式が中断マーカーであり、そしてnext
この方法は、実行を再開することができます。
yield
ジェネレータ式はなり他の場所でエラーが発生して関数内でのみ使用することができます。
VAR ARR = [1、[2]、[3]、[4]、[5、6]。
VARフラット=関数*(A){
。forEachの(関数(アイテム){
IF(typeof演算項目== '番号'){!
収率*フラット(項目);
}そうでなければ{
収率項目;
}
})。
}。
{(フラット(ARR)のF VAR)のため
にconsole.log(F)。
}
それはforEachの中であるため(のforEach自体は関数であり、それはエラーになります)
yield
あなたは別の式で式を使用する場合、それは括弧の内側に配置する必要があります。
機能*デモ(){ はconsole.log( 'こんにちは' +収率)。//にSyntaxError にconsole.log( 'こんにちは' + 123をもたらします); //にSyntaxError にconsole.log( 'こんにちは' +(収率)); // OK にconsole.log( 'こんにちは' +(収量123)); // OK }
yield
表現関数パラメータまたは上の割り当ての右側には、あなたはブラケットを追加することはできません。
関数*デモ(){ FOO(収率''、収率'B')。// OK 入力=収量をしましょう。// OK }
次のメソッドパラメータ:次の方法は、このパラメータは、実行時収量の初期値として使用される、パラメータを渡すことができる(パラメータが渡されない場合、デフォルトは未定義です)
関数* FOO(X){ VARのY = 2 * (収率(X + 1)) 。 VaRのZ = 収率(Y / 3) 。 リターン(X + Y + Z)。 } VAR A = FOO(5)。 a.next()//オブジェクト{値:6、行わ:偽} a.next()//オブジェクト{値:NaNで行わ:偽} a.next()//オブジェクト{値:NaNで行わ:真} VARのB = FOO(5)。 b.next()// {値:6、行わ:偽} b.next(12)// {値:8、行わ:偽} b.next(13)// {値:42、行わ:真}
主として(b)に示すように、最初のコールは次の6の降伏値、次回実行二時間後、収率を設定し、この時間は、赤いマークの上に私の場所であり、12は第1のインスタンスを参照する(収率( 1 + X)) 12は、従って、Y = 24、第二行の収率は、ケースリターン収率8、第三の方法の次の呼び出しのように、設定値が13である場合、赤いマークの場所です= 収率(Y / 3)以上の結果、リターンにその後13、Z値13 =は、現時点で実行され、xは5であり、yが24であり、zは42を加えた、13です。
収率は式が続いている場合、初期値は、式の値を表している、すなわち、歩留まりが続きます。上記のパラメータの例は、二回目の次のメソッドの呼び出しは、デフォルト値の収率が定義されていないので渡すので、NaNでの値がなかったです
以下のための...の:あなたが歩くことができるように、イテレータGeneratorは、実行時に生成されますので、
関数*のFOO(){ 収率1。 2を得ました。 3が得られます。 4を得ました。 5が得られます。 6を返します。 } {(FOOのV()とする)のために はconsole.log(V)を、 } // 1 2 3 4 5
オブジェクトトラバース:
* objectEntries関数(OBJ){ LETはpropKeysの=のReflect.ownKeys(OBJ); //キーのオブジェクトを取得 するための(propKeysのpropKeyを聞かせて){//ループを実行し、キー値に戻る 収率[propKey、OBJ [propKey]。 } } LET {まず=ジェーン: 'ジェーン'、最終'ドゥ'}; (objectEntries(ジェーン)のLET [キー、値])のための{ にconsole.log(キー} {$ `` $ {値}) ; }
反復子オブジェクト自体が発生するので、それは、直接オブジェクトに割り当てることができSymbol.iteratorインターフェース
関数* objectEntries(){ せpropKeys = Object.keys(本) (LET propKeysのpropKey){ため 収量[propKey、この[propKey]。 } } せジェーン= {最初'ジェーン'、最後: 'ドゥ'}。ジェーン[Symbol.iterator] =のobjectEntries。 {(ジェーンの[キー値]せて)のために はconsole.log( `$ {キー} $ {値}`)。 } //最初:ジェーン //最後:ドウ
x.throw:例外をスロー(トライキャッチキャプチャを使用することを忘れないでください)
関数G * = VAR(){ 試み{ 収率; }キャッチ(E){ にconsole.log( 'トラップの内部'、E); } }; VAR I = G(); i.next(); 試み{ I.投( 'A'); i.throw( 'B'); }キャッチ(E){ にconsole.log( '外部キャプチャ'、E); } //トラップAの内部 //外部キャプチャB
キャッチは、後者の値が得られ、一度だけキャプチャし、キャプチャに戻ります
トライキャッチでの歩留まり場合は、返しません。
2トライキャッチでの歩留まり(及び主要な実施形態トラバーサルのトラバースには影響しませんが、出力4、エラーの後に続く)ので2、戻りません。
インビトロ、インビボ機能をスロー生成関数エラーを捕捉することができ、次いで、インビトロ機能において、インビボで生成関数をスローエラーがあってもよいcatch
取り込ま。
プロセスジェネレータの実行エラーがスローされると、内部に捕捉されない、それはもう実行しません。あなたは、その後も起動する場合はnext
この方法が返されますvalue
と同じプロパティをundefined
、done
に等しいプロパティtrue
、オブジェクト、JavaScriptエンジンが実行されているジェネレータは、このオーバーだと思います。
return:终结遍历Generator
function* gen() { yield 1; yield 2; yield 3; } var g = gen(); g.next() // { value: 1, done: false } g.return('foo') // { value: "foo", done: true } g.next() // { value: undefined, done: true }
yield* 表达式:在 Generator 函数内部,调用另一个 Generator 函数。
对比:
function* bar() { yield 'x'; yield* foo(); yield 'y'; } // 等同于 function* bar() { yield 'x'; yield 'a'; yield 'b'; yield 'y'; } // 等同于 function* bar() { yield 'x'; for (let v of foo()) { yield v; } yield 'y'; } for (let v of bar()){ console.log(v); } // "x" // "a" // "b" // "y"
另外因为Generator 会生成一个遍历器对象,所以,会有下面这样的不同
function* inner() { yield 'hello!'; } function* outer1() { yield 'open'; yield inner(); yield 'close'; } var gen = outer1() gen.next().value // "open" gen.next().value // 返回一个遍历器对象 gen.next().value // "close" function* outer2() { yield 'open' yield* inner() yield 'close' } var gen = outer2() gen.next().value // "open" gen.next().value // "hello!" gen.next().value // "close"
out1 在执行到第二行的时候返回的是一个遍历器对象。
任何数据结构只要有 Iterator 接口,就可以被yield*
遍历。
yield*
后面的 Generator 函数(没有return
语句时),等同于在 Generator 函数内部,部署一个for...of
循环。(如果有return,那么return的值不会被for ...of 侦测到,但是可以作为想代理它的Generator 函数返回数据。)
function* concat(iter1, iter2) { yield* iter1; yield* iter2; } // 等同于 function* concat(iter1, iter2) { for (var value of iter1) { yield value; } for (var value of iter2) { yield value; } }
function* foo() { yield 2; yield 3; return "foo"; } 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}
对象属性:
let obj = { * myGeneratorMethod() { ··· } };
let obj = {
myGeneratorMethod: function* () { // ··· } };
协程:相比线程,由于 JavaScript 是单线程语言,只能保持一个调用栈。引入协程以后,每个任务可以保持自己的调用栈。这样做的最大好处,就是抛出错误的时候,可以找到原始的调用栈。不至于像异步操作的回调函数那样,一旦出错,原始的调用栈早就结束。
如果将 Generator 函数当作协程,完全可以将多个需要互相协作的任务写成 Generator 函数,它们之间使用yield
表达式交换控制权。同一时间可以有多个线程处于运行状态,但是运行的协程只能有一个,其他协程都处于暂停状态。此外,普通的线程是抢先式的,到底哪个线程优先得到资源,必须由运行环境决定,但是协程是合作式的,执行权由协程自己分配。
(也就是说可以自己控制)。
异步操作同步化表达:Generator 函数的一个重要实际意义就是用来处理异步操作,改写回调函数。
function* loadUI() { showLoadingScreen(); yield loadUIDataAsynchronously(); hideLoadingScreen(); } var loader = loadUI(); // 加载UI loader.next() // 卸载UI loader.next()
不用回调函数一个一个套了,看着多清晰~~
控制流:
一般如果异步操作,我们以前会这样做,一直套
step1(function (value1) { step2(value1, function(value2) { step3(value2, function(value3) { step4(value3, function(value4) { // Do something with value4 }); }); }); });
Promise写法:
Promise.resolve(step1) .then(step2) .then(step3) .then(step4) .then(function (value4) { // Do something with value4 }, function (error) { // Handle any error from step1 through step4 }) .done();
已经很清晰了。
function* longRunningTask(value1) { try { var value2 = yield step1(value1); var value3 = yield step2(value2); var value4 = yield step3(value3); var value5 = yield step4(value4); // Do something with value4 } catch (e) { // Handle any error from step1 through step4 } }
scheduler(longRunningTask(initialValue));
function scheduler(task) {
var taskObj = task.next(task.value);
// 如果Generator函数未结束,就继续调用
if (!taskObj.done) {
task.value = taskObj.value
scheduler(task);
}
}
更加趋于函数式编程。