ジェネレータに関する私の理解は本質的に間違っていると確信しています。オンライン リソースはすべて矛盾しているようで、学習体験が非常に困難で混乱したものになっています。
私の理解では、このyield
キーワードは、現在実行中のコードブロックで値を待つ残りのコードをコールバック内で実行するのではなく、ほとんどのチュートリアルで指摘されているように、次のように使用できます。
(function *() {
// Wait until users have be got and put into value of `results`
var results = yield db.get("users");
// And continue
view.display(results);
})();
の代わりに:
db.get("user", function(results) {
view.display(results);
});
そうです、自分でジェネレーターを書こうとするまでは、すべて順調です。しかし、いくつかの問題に遭遇しました。
- 上記の最初のサンプル コードは、ジェネレーターを反復処理するものがないため実行されません。何らかの高次の存在が
.next
どこかを呼び出す必要があります。 - ジェネレータをサポートするには、I/O 呼び出しに至るまで API 全体を書き直す必要がありますか?
- 私が理解しているところによると、
yield
それは値を待つ最も一般的な使用例ですが、実装部分(読み:戻り値/内部db.get
)ではyield
、この値を現在待機中のブロックに送り返して実行を再開します。
たとえば次のようになります。
function *fn() {
yield 1;
yield "a";
}
var gen = fn();
gen.next(); // 1
gen.next(); // "a";
yield
このコンテキストでは、 は結果を待つのではなく、値を送り返しています。上記の最初の例では、 からの結果を待ってからdb.get
、 は値を「返す」または送り返すのではなく、実行を再開します。このdb.get
ケースが正しい場合、これは本質的に同期的ではないでしょうか。つまり、次のコードとまったく同じではないでしょうか。
(function() {
//Wait for the results
var results = fs.readFileSync("users.txt");
// Use results
view.display(results);
})();
残念ながら、この質問から明らかなのは(おそらく唯一明らかなのは)、私がジェネレータを理解していないということです。うまくいけば、ここで何らかの洞察が得られるかもしれません。
ベストアンサー1
TL;DR: ジェネレータの本質はコード実行の一時停止を制御します。
ジェネレータ自体については、これ。
まとめると、区別すべきコンポーネントは3つあります。1. ジェネレータ関数 2. ジェネレータ 3. 生成された結果
ジェネレーター関数は、単にfunction
先頭に星があり、yield
本体に (オプション) があるものです。
function *generator() {
console.log('Start!');
var i = 0;
while (true) {
if (i < 3)
yield i++;
}
}
var gen = generator();
// nothing happens here!!
ジェネレータ関数自体は、上記のケースではジェネレータを返す以外何もしません。gen
返された後にのみコンソール出力がないため、ここでは出力されません。発生器のnext
メソッドは、ジェネレータ関数が実行されます。ジェネレータにはいくつかのメソッドがありますが、最も重要なのはですnext
。next
コードを実行し、ジェネレータ結果。
var ret = gen.next();
// Start!
console.log(ret);
// {value: 0, done: false}
ret
上記はジェネレータ結果value
2つの特性があります。ジェネレータ関数、およびdone
、ジェネレータ関数戻る。
console.log(gen.next());
// {value: 1, done: false}
console.log(gen.next());
// {value: 2, done: false}
console.log(gen.next());
// {value: undefined, done: true}
この時点では、少なくともジェネレータの非同期機能については、誰もあなたがジェネレータを理解していることを期待していません。
簡単に言えば、ジェネレータには 2 つの機能があります。
- 関数から抜け出して、いつ関数に戻るかを外部のコードに決定させることを選択できます。
- 非同期呼び出しの制御はコード外で行うことができます
コード内で、yield
関数の外部にジャンプし、next(val)
関数に戻って値を関数に戻します。外部のコードは非同期呼び出しを処理し、独自のコードに切り替える適切なタイミングを決定できます。
サンプルをもう一度見てみましょう:
var gen = generator();
console.log('generated generator');
console.log(gen.next().value);
// mock long long processing
setTimeout(function() {
console.log(gen.next().value);
console.log('Execute after timer fire');
}, 1000);
console.log('Execute after timer set');
/* result:
generated generator
start
0
Execute after timer set
1
Execute after timer fire
*/
わかりましたか? ジェネレータ関数自体はコールバックを処理しません。 外部のコードが処理します。
ベースはここにあります。このコードを詳細化して、同期のようなジェネレーター機能を維持しながら、完全な非同期性をサポートできます。
たとえば、オブジェクトgeturl
を返す非同期呼び出しがあるとしますpromise
。var html = yield getUrl('www.stackoverflow.com');
これはコードの外にジャンプします。そして、外部のコードは次のようなことを行います。
var ret = gen.next();
ret.then(function (fetchedHTML) {
// jumps back to your generator function
// and assign fetchHTML to html in your code
gen.next(fetchedHTML);
});