ホームページ > 記事 > ウェブフロントエンド > Javascriptのジェネレータ関数とyieldキーワード
注文
Javascript で誰もが最も議論するのは、非同期プログラミングの操作と、コールバックの多重ネストを回避する方法です。非同期操作のコールバックが多数ネストされると、コードが肥大化するだけでなく、エラーも発生しやすくなります。有名な Promise 社など、さまざまな非同期プログラミング ソリューションが常に提案されています。今日説明するジェネレーターとyieldは非同期プログラミングに関連しており、非同期プログラミングの同期に役立ちます。
ジェネレーターの概要
ジェネレーターは、関数と関数名の間に余分な * があることを除いて、フォーム上の関数に似ています。 yield キーワードはジェネレーター内で使用する必要があります。例:
function * gen(){ var result1 = yield 'hello'; var result2 = yield 'world'; return result1 + result2; }
ジェネレーター関数が呼び出されると、関数内のコードは実行されませんが、next メソッドを含むトラバーサーが返されます。次のメソッドが実行されるたびに、Generator 関数本体は実行を開始し、yield ステートメントに到達してステートメントを実行し、ここで一時停止します。使用法は次のとおりです。
var g = gen(); g.next(1); //{value : 'hello', done : false} g.next(2); //{value : 'world', done : false} g.next(); //{value : 3, done: true} g.next(); //{value : undefined, done: true}
next メソッドを呼び出すと、オブジェクトが返されます。このオブジェクトには、value と Done という 2 つの属性が含まれています。Value は、現在の yield ステートメントの値です。 Done は、ジェネレーター関数本体が実行されたかどうかを示します。次のメソッドはパラメーターも受け入れます。このパラメーターは、yield ステートメントの戻り値として使用され、後続のプログラムで使用できます。プログラムの実行が終了するか、return ステートメントに遭遇すると、value は関数本体の戻り値となり、done は true になります。この時点で、次のメソッドが再度実行されると、value は未定義になり、done は true のままになります。
トラバーサルにおけるジェネレーターの応用
js では、配列をトラバースしたい場合、for...of のようなステートメントを使用してトラバースできます。これは、実際には配列にジェネレーター トラバーサーが含まれているためです。自己定義オブジェクトにトラバーサも含まれている場合は、for...of などのトラバーサル ステートメントを通じてカスタム オブジェクトをトラバースできます。このイテレータは、Symbol.iterator プロパティに保存されます。
var myArray = { 0: '你', 1: '的', 2: '名字', length: 3 }; myArray[Symbol.iterator] = function * (){ for(var i = 0; i < this.length; i++) { yield this[i]; } }; for(var item of myArray) { console.log(item); } //你 //的 //名字
非同期プログラミングにおけるジェネレーターの応用
Javascript の核心は非同期プログラミングであり、各非同期操作は実行結果を返すコールバック関数を提供します。いくつかの操作があり、後の操作が前の操作の結果に依存するとします。
step1(function(value1) { step2(value1, function(value2) { step3(value2, function(value3)) { //some code } }); })
このようなコードでは、ネストされたコールバックの数が増えると、プログラムが非常に理解しにくくなります。私たちがしなければならないのは、コールバックをフラット化することです。 Promise オブジェクトには、上記の操作を約束します。
step1().then(function(value1){ return step2(value1); }).then(function(value2){ return step3(value2); }).then(function(){ //some code })
入れ子が少なくなっていることがわかりますが、これは、非同期操作を同期操作に変えることができる場合、つまり、入れ子なしで行うことができる場合には、最も理想的な解決策ではありません。プログラムが理解しやすくなります。ジェネレーター機能はそのような機会を提供します。
function *workflow(){ var value1 = yield step1(); var value2 = yield step2(); var value3 = yield step3(); //some code }
これが私たちが望む結果であり、非同期プログラミング プログラムは同期プログラミングの形式です。次に行う必要があるのは、このジェネレーターを実行させることなので、エグゼキューターが必要です。 co はエグゼキュータであり、Generator を自動的に実行できます。
co(function *workflow(){ var value1 = yield step1(); var value2 = yield step2(); var value3 = yield step3(); //some code });
co には制限があります。yield ステートメントの後には Promise オブジェクトまたは Thunk 関数のみを続けることができます。co の詳細については、Ruan 先生の記事「co 関数ライブラリの意味と使用法」を参照してください。ただし、この方法でも外部ライブラリ関数に依存する必要があるため、ES6 では async および await キーワードが提案されました。 async と await は実際には Generator の構文シュガーです。専用のアクチュエーターが付属しているだけです。上記のコードを非同期形式に書き換えます:
async function workflow(){ var value1 = await step1(); var value2 = await step2(); var value3 = await step3(); //some code } var result = workflow();
async没有了co的限制。await关键字后面可以跟 Promise 对象和原始类型的值(数值、字符串和布尔值,但这时等同于同步操作)。