Javascriptジェネレーター: 理解する 質問する

Javascriptジェネレーター: 理解する 質問する

ジェネレータに関する私の理解は本質的に間違っていると確信しています。オンライン リソースはすべて矛盾しているようで、学習体験が非常に困難で混乱したものになっています。

私の理解では、この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メソッドは、ジェネレータ関数が実行されます。ジェネレータにはいくつかのメソッドがありますが、最も重要なのはですnextnextコードを実行し、ジェネレータ結果

var ret = gen.next();
// Start!
console.log(ret);
// {value: 0, done: false}

ret上記はジェネレータ結果value2つの特性があります。ジェネレータ関数、および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を返す非同期呼び出しがあるとしますpromisevar 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);
});

より詳しいガイドについては、これ. そしてリポジトリのような共同銀河つるすや。。など。

おすすめ記事