JavaScriptで「yield」キーワードについて聞いたことがありますが、それについてのドキュメントは非常に貧弱です。誰かがその使用法とその使用目的を説明してくれますか(または説明するサイトを勧めます)。
JavaScriptで「yield」キーワードについて聞いたことがありますが、それについてのドキュメントは非常に貧弱です。誰かがその使用法とその使用目的を説明してくれますか(または説明するサイトを勧めます)。
回答:
MDNドキュメントは IMO、かなり良いです。
yieldキーワードを含む関数はジェネレーターです。これを呼び出すと、その仮パラメーターは実際の引数にバインドされますが、本体は実際には評価されません。代わりに、ジェネレーターイテレーターが返されます。ジェネレーター/イテレーターのnext()メソッドを呼び出すたびに、反復アルゴリズムを通じて別のパスが実行されます。各ステップの値は、yieldキーワードで指定された値です。yieldは、アルゴリズムの各反復間の境界を示すreturnのジェネレーターイテレーターバージョンと考えてください。next()を呼び出すたびに、生成コードは、yieldに続くステートメントから再開します。
遅ればせながら、おそらく誰もがyield
今は知っていますが、いくつかのより良いドキュメントがやって来ました。
James Longによる"Javascript's Future:Generators"の例を公式のHarmony標準に適合させます。
function * foo(x) {
while (true) {
x = x * 2;
yield x;
}
}
「fooを呼び出すと、次のメソッドを持つジェネレーターオブジェクトが返されます。」
var g = foo(2);
g.next(); // -> 4
g.next(); // -> 8
g.next(); // -> 16
だから、yield
一種のようであるreturn
:あなたが何かを取り戻します。 return x
値を返しますx
が、yield x
次の値に向かって反復するためにあなたの方法を与える関数を返します。反復中に中断する可能性のある、メモリを大量に消費する可能性のある手順がある場合に役立ちます。
function* foo(x){
が
*
トークンが追加されます。必要かどうかは、戻ってくる将来の種類によって異なります。詳細は長いです:GvRは、Javascript実装がモデル化されているPython実装について説明しています。使用すると、function *
いくつかのケースではわずかに多くのオーバーヘッドよりも関わらず、常に、右のだろうfunction
とyield
。
function *
とyield
し、引用エラー(「収量又は収率*発現は、非ジェネレータ関数で発生した場合に早期にエラーが発生した」)を添加しました。ただし、Firefoxの元のJavascript 1.7実装では、は必要ありませんでした*
。それに応じて回答を更新しました。ありがとう!
それは本当にシンプルです、これはそれがどのように機能するかです
yield
キーワードは、非同期でいつでも関数を一時停止および再開するのに役立ちます。この単純なジェネレータ関数を見てみましょう:
function* process() {
console.log('Start process 1');
console.log('Pause process2 until call next()');
yield;
console.log('Resumed process2');
console.log('Pause process3 until call next()');
let parms = yield {age: 12};
console.log("Passed by final process next(90): " + parms);
console.log('Resumed process3');
console.log('End of the process function');
}
let _process = process();
あなたが呼び出すまで)(_process.nextをそれは文句を言わない実行します最初の2行のコードのを、その後、最初の歩留まりがします一時停止機能を。次の一時停止ポイント(yieldキーワード)まで関数を再開するには、_process.next()を呼び出す必要があります。
複数のyieldは、単一の関数内のJavaScriptデバッガのブレークポイントであると考えることができます。次のブレークポイントをナビゲートするように指示するまで、コードブロックは実行されません。(注:アプリケーション全体をブロックせずに)
ただし、yieldがこの一時停止と再開の動作を実行している間、結果を返すこともできますが{value: any, done: boolean}
、前の関数では値を出力していません。以前の出力を調べると、undefinedの{ value: undefined, done: false }
値で同じことが表示されます。
yieldキーワードを詳しく見てみましょう。オプションで、式を追加し、デフォルトのオプション値を割り当てるように設定できます。(公式ドキュメント構文)
[rv] = yield [expression];
expression:ジェネレーター関数から返す値
yield any;
yield {age: 12};
rv:ジェネレータのnext()メソッドに渡されたオプションの値を返します
このメカニズムを使用してパラメーターをprocess()関数に渡すだけで、さまざまな歩留りパーツを実行できます。
let val = yield 99;
_process.next(10);
now the val will be 10
使い方
参照:
Nick Sotirosの答え(私は素晴らしいと思います)を単純化/詳しく説明します。コーディングを開始する方法を説明するのが最善だと思います yield
ます。
私の意見では、使用の最大の利点は yield
、コードに見られるネストされたコールバックの問題をすべて排除できることです。最初はどのように見るのが難しいので、私はこの答えを書くことにしました(私自身、そしてできれば他の人のために!)
それを行う方法は、必要なものを取得するまで自発的に停止/一時停止できる関数であるコルーチンのアイデアを導入することです。JavaScriptでは、これはで示されfunction*
ます。function*
関数だけが使用できますyield
。
ここにいくつかの典型的なjavascriptがあります:
loadFromDB('query', function (err, result) {
// Do something with the result or handle the error
})
すべてのコード(明らかにこのloadFromDB
呼び出しを待機する必要がある)は、この醜く見えるコールバック内にある必要があるため、これは不格好です。これはいくつかの理由で悪いです...
})
どこにでも追跡する必要をいますfunction (err, result)
専門用語result
一方、ではyield
、これらすべてを1行で実行できます。これには、優れたコルーチンフレームワークが役立ちます。
function* main() {
var result = yield loadFromDB('query')
}
これで、変数や物が読み込まれるのを待つ必要があるときに、メイン関数が必要に応じて生成されます。しかし、これを実行するには、通常の(非コルーチン関数)を呼び出す必要があります。単純なコルーチンルーチンフレームワークはこの問題を修正できるので、これを実行するだけです:
start(main())
そして、開始が定義されています(Nick Sotiroの回答から)
function start(routine, data) {
result = routine.next(data);
if(!result.done) {
result.value(function(err, data) {
if(err) routine.throw(err); // continue next iteration of routine with an exception
else start(routine, data); // continue next iteration of routine normally
});
}
}
そして今、あなたははるかに読みやすく、削除が簡単で、インデントや関数などをいじる必要のない美しいコードを手に入れることができます。
興味深い所見は、この例でyield
は、実際にはコールバックを使用する関数の前に置くことができる単なるキーワードであるということです。
function* main() {
console.log(yield function(cb) { cb(null, "Hello World") })
}
「Hello World」と印刷します。したがって、次のようにyield
、同じ関数シグネチャ(cbなし)を作成してを返すだけで、実際にコールバック関数を使用することができますfunction (cb) {}
。
function yieldAsyncFunc(arg1, arg2) {
return function (cb) {
realAsyncFunc(arg1, arg2, cb)
}
}
function*
は利回りのない通常の関数ですか?
function *
それは利回りを含む関数だと思います。これはジェネレーターと呼ばれる特別な関数です。
yield
どこでも使用している人にとって、これはコールバックよりも理にかなっていると確信していますが、これがコールバックよりも読みやすいかどうかはわかりません。
完全な答えを与えるために:yield
はに似てreturn
いますが、ジェネレーターで動作します。
一般的に示されている例と同様に、これは次のように機能します。
function *squareGen(x) {
var i;
for (i = 0; i < x; i++) {
yield i*i;
}
}
var gen = squareGen(3);
console.log(gen.next().value); // prints 0
console.log(gen.next().value); // prints 1
console.log(gen.next().value); // prints 4
しかし、yieldキーワードの2番目の目的もあります。ジェネレーターに値を送信するために使用できます。
明確にするために、小さな例:
function *sendStuff() {
y = yield (0);
yield y*y;
}
var gen = sendStuff();
console.log(gen.next().value); // prints 0
console.log(gen.next(2).value); // prints 4
これ2
は、値がに割り当てられているためy
、最初の歩留まり(が返された0
)で停止した後、ジェネレーターに送信することによって機能します。
これにより、本当にファンキーなものをいくつか作成することができます。(コルーチンを検索)
イテレータージェネレーターに使用されます。基本的には、手続き型コードを使用して(潜在的に無限の)シーケンスを作成できます。Mozillaのドキュメントを参照してください。
yield
コルーチンフレームワークを使用して、コールバック地獄を排除するために使用することもできます。
function start(routine, data) {
result = routine.next(data);
if(!result.done) {
result.value(function(err, data) {
if(err) routine.throw(err); // continue next iteration of routine with an exception
else start(routine, data); // continue next iteration of routine normally
});
}
}
// with nodejs as 'node --harmony'
fs = require('fs');
function read(path) {
return function(callback) { fs.readFile(path, {encoding:'utf8'}, callback); };
}
function* routine() {
text = yield read('/path/to/some/file.txt');
console.log(text);
}
// with mdn javascript 1.7
http.get = function(url) {
return function(callback) {
// make xhr request object,
// use callback(null, resonseText) on status 200,
// or callback(responseText) on status 500
};
};
function* routine() {
text = yield http.get('/path/to/some/file.txt');
console.log(text);
}
// invoked as.., on both mdn and nodejs
start(routine());
yieldキーワードを使用したフィボナッチ数列ジェネレーター。
function* fibbonaci(){
var a = -1, b = 1, c;
while(1){
c = a + b;
a = b;
b = c;
yield c;
}
}
var fibonacciGenerator = fibbonaci();
fibonacciGenerator.next().value; // 0
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 1
fibonacciGenerator.next().value; // 2
Yeild
javaScript関数のキーワードはそれをジェネレーターにします。
javaScriptのジェネレーターとは
ジェネレータは、単一の値ではなく一連の結果を生成する関数です。つまり、一連の値を生成します
つまり、ジェネレーターはヘルプイテレーターと非同期で動作するのに役立ちます。本当に?
イテレータとは、アイテムに一度に1つずつアクセスできるという意味です
イテレータが一度に1つのアイテムにアクセスするのに役立つ場所 ジェネレータ関数を介してアイテムにアクセスするのに役立ちます
ジェネレータ関数はyeild
、キーワードを使用する関数であり、yieldキーワードは関数の実行の一時停止と再開に役立ちます
ここに簡単な例があります
function *getMeDrink() {
let question1 = yield 'soda or beer' // execution will pause here because of yield
if (question1 == 'soda') {
return 'here you get your soda'
}
if (question1 == 'beer') {
let question2 = yield 'Whats your age' // execution will pause here because of yield
if (question2 > 18) {
return "ok you are eligible for it"
} else {
return 'Shhhh!!!!'
}
}
}
let _getMeDrink = getMeDrink() // initialize it
_getMeDrink.next().value // "soda or beer"
_getMeDrink.next('beer').value // "Whats your age"
_getMeDrink.next('20').value // "ok you are eligible for it"
_getMeDrink.next().value // undefined
何が起こっているのか簡単に説明しましょう
各yeild
キーワードで実行が一時停止されていることに気付きました。yield
イテレータの助けを借りて、最初にアクセスできます。.next()
これyield
は、一度にすべてのキーワードに対して反復しyield
、単純な単語でキーワードが残っていない場合はundefinedを返します。yield
キーワードは、関数が一時停止するたびにブレークポイントであり、イテレータを使用して呼び出すときにのみ再開するブレークポイントます
私たちの場合:_getMeDrink.next()
これは、関数の各ブレークポイントにアクセスするのに役立つイテレータの例です
ジェネレーターの例:
async/await
async/await
あなたが実装を見ると、あなたは仕事generator functions & promises
をするのに使われてasync/await
いるのを見るでしょう
提案は歓迎されていることを指摘してください
非同期JavaScript呼び出し間の依存関係。
歩留まりを使用できる別の良い例。
function request(url) {
axios.get(url).then((reponse) => {
it.next(response);
})
}
function* main() {
const result1 = yield request('http://some.api.com' );
const result2 = yield request('http://some.otherapi?id=' + result1.id );
console.log('Your response is: ' + result2.value);
}
var it = main();
it.next()
イールドについて学ぶ前に、ジェネレータについて知る必要があります。ジェネレーターはfunction*
構文を使用して作成されます。ジェネレーター関数はコードを実行せず、代わりにジェネレーターと呼ばれるタイプのイテレーターを返します。next
メソッドを使用して値が指定されると、ジェネレーター関数は、yieldキーワードが見つかるまで実行を続けます。を使用yield
すると、2つの値を含むオブジェクトが返されます。1つは値で、もう1つは実行されます(ブール値)。値は、配列、オブジェクトなどにすることができます。
簡単な例:
const strArr = ["red", "green", "blue", "black"];
const strGen = function*() {
for(let str of strArr) {
yield str;
}
};
let gen = strGen();
for (let i = 0; i < 5; i++) {
console.log(gen.next())
}
//prints: {value: "red", done: false} -> 5 times with different colors, if you try it again as below:
console.log(gen.next());
//prints: {value: undefined, done: true}
また、yieldキーワードを理解しようとしています。私の現在の理解に基づいて、ジェネレーターでは、yieldキーワードはCPUコンテキストスイッチのように機能します。yieldステートメントを実行すると、すべての状態(ローカル変数など)が保存されます。
これに加えて、{value:0、done:false}のような直接の結果オブジェクトが呼び出し元に返されます。呼び出し元は、この結果オブジェクトを使用して、next()を呼び出すことでジェネレータを再び「ウェイクアップ」するかどうかを決定できます(next()を呼び出すことは実行を繰り返すことです)。
もう1つの重要なことは、ローカル変数に値を設定できることです。この値は、ジェネレーターを「ウェイクアップ」するときに「next()」呼び出し元から渡すことができます。たとえば、次のようにit.next( 'valueToPass'): "resultValue = yield slowQuery(1);" 次の実行を起こすときと同様に、呼び出し側は実行結果を実行に注入できます(ローカル変数に注入します)。したがって、この実行には2種類の状態があります。
最後の実行で保存されたコンテキスト。
この実行のトリガーによって注入された値。
したがって、この機能により、ジェネレーターは複数の非同期操作を整理できます。最初の非同期クエリの結果は、ローカル変数(上記の例ではresultValue)を設定することにより、2番目のクエリに渡されます。2番目の非同期クエリは、最初の非同期クエリの応答によってのみトリガーできます。次に、ローカル変数は最初のクエリの応答から挿入された値であるため、2番目の非同期クエリはローカル変数値をチェックして次の手順を決定できます。
非同期クエリの難点は次のとおりです。
コールバック地獄
コールバックでパラメータとして渡さない限り、コンテキストが失われます。
収量と発電機は両方で役立ちます。
収量とジェネレーターがない場合、複数の非同期クエリを並べ替えるには、読み取りと保守が容易ではないコンテキストとしてパラメーターを持つネストされたコールバックが必要です。
以下は、nodejsで実行される連鎖非同期クエリの例です。
const axios = require('axios');
function slowQuery(url) {
axios.get(url)
.then(function (response) {
it.next(1);
})
.catch(function (error) {
it.next(0);
})
}
function* myGen(i=0) {
let queryResult = 0;
console.log("query1", queryResult);
queryResult = yield slowQuery('https://google.com');
if(queryResult == 1) {
console.log("query2", queryResult);
//change it to the correct url and run again.
queryResult = yield slowQuery('https://1111111111google.com');
}
if(queryResult == 1) {
console.log("query3", queryResult);
queryResult = yield slowQuery('https://google.com');
} else {
console.log("query4", queryResult);
queryResult = yield slowQuery('https://google.com');
}
}
console.log("+++++++++++start+++++++++++");
let it = myGen();
let result = it.next();
console.log("+++++++++++end+++++++++++");
以下は実行結果です:
+++++++++++ start +++++++++++
query1 0
+++++++++++ end +++++++++++
query2 1
query4 0
以下の状態パターンは、上記の例と同様のことができます。
const axios = require('axios');
function slowQuery(url) {
axios.get(url)
.then(function (response) {
sm.next(1);
})
.catch(function (error) {
sm.next(0);
})
}
class StateMachine {
constructor () {
this.handler = handlerA;
this.next = (result = 1) => this.handler(this, result);
}
}
const handlerA = (sm, result) => {
const queryResult = result; //similar with generator injection
console.log("query1", queryResult);
slowQuery('https://google.com');
sm.handler = handlerB; //similar with yield;
};
const handlerB = (sm, result) => {
const queryResult = result; //similar with generator injection
if(queryResult == 1) {
console.log("query2", queryResult);
slowQuery('https://1111111111google.com');
}
sm.handler = handlerC; //similar with yield;
};
const handlerC = (sm, result) => {
const queryResult = result; //similar with generator injection;
if (result == 1 ) {
console.log("query3", queryResult);
slowQuery('https://google.com');
} else {
console.log("query4", queryResult);
slowQuery('https://google.com');
}
sm.handler = handlerEnd; //similar with yield;
};
const handlerEnd = (sm, result) => {};
console.log("+++++++++++start+++++++++++");
const sm = new StateMachine();
sm.next();
console.log("+++++++++++end+++++++++++");
実行結果は次のとおりです。
+++++++++++ start +++++++++++
query1 0
+++++++++++ end +++++++++++
query2 1
query4 0