web-dev-qa-db-ja.com

node.jsのジェネレーター/ yieldを理解しようとしています-非同期関数を実行するものは何ですか?

Node.jsにジェネレーターが追加されました。

私の理解では、ジェネレーターを使用して、はるかに線形に見えるコードを記述し、Doomスタイルのコーディングのコールバック地獄やピラミッドを回避できます。

したがって、これまでのところ、ジェネレーター内では、コードは「yield」ステートメントに到達するまで実行されると理解しています。ジェネレーター関数の実行は、この時点で一時停止します。 yieldステートメントは、関数である可能性のある戻り値を指定します。通常、これはブロッキングI/O関数であり、通常は非同期で実行する必要があります。

歩留まりの戻り関数は、ジェネレーターと呼ばれるものに返されます。

私の質問は、この時点で何が起こるかということです。歩留まりが返したブロッキングI/O関数を正確に実行するものは何ですか?

線形に見えるジェネレーター/ yieldコードを作成するには、ジェネレーターを呼び出す特定の種類の関数、ジェネレーターをループして、yieldによって返される各非同期関数を実行して非同期関数の結果をジェネレーターに戻しますか?

イールドによって返される非同期関数がどのように実行されるかは、まだ正確にはわかりません。ジェネレーターを呼び出す関数によって実行される場合、非同期で実行されますか?そうしないと、動作がブロックされるため、そうだと思います。

私の質問を要約すると:

  1. ジェネレーターを使用して「線形」非同期コードを作成するには、ジェネレーターを反復処理し、生成された関数をコールバックとして実行し、コールバックの結果をジェネレーターに返す呼び出し関数が必要ですか?
  2. 質問1の答えが「はい」の場合、生成された関数は正確にどのように実行されますか?非同期ですか?

誰かがプロセス全体がどのように機能するかについてのより良い概要/要約を提供できますか?

17
Duke Dougal

ジェネレーターを使用して非同期コードを作成する場合、次の2種類の関数を処理します。

  • 通常functionで宣言された関数。これらの関数できません yield。それらは完全に実行されるため、それらと同期スタイルで非同期コードを記述することはできません。非同期完了は、コールバックを介してのみ処理できます(node-fibersライブラリやコード変換などの追加の機能を呼び出さない限り)。
  • generatorfunction*で宣言された関数。これらの関数can yield。それらが降伏する可能性があるので、それらの中に同期スタイルで非同期コードを書くことができます。ただし、ジェネレーターを作成し、コールバックを処理し、コールバックが発生するたびにnext呼び出しでgeneratorを再開するコンパニオン関数が必要です。

コンパニオン関数を実装するライブラリがいくつかあります。これらのライブラリのほとんどでは、コンパニオン関数は一度に1つのfunction*を処理し、コード内のすべてのfunction*の周りにラッパーを配置する必要があります。 (私が書いた)ギャラクシーライブラリは、中間ラッパーなしで他のfunction*を呼び出すfunction*を処理できるため、少し特別です。コンパニオン関数は、ジェネレーターのスタックを処理する必要があるため、少し注意が必要です。

yield/nextとコンパニオン関数の間のfunction*ダンスが小さいため、実行フローを理解するのが難しい場合があります。フローを理解する1つの方法は、選択したライブラリを使用して例を記述し、コードとライブラリの両方にconsole.logステートメントを追加して、それを実行することです。

12
Bruno Jouhier

[ブロッキングIO関数]がジェネレーターを呼び出す関数によって実行される場合、非同期で実行されますか?そうしないと、動作がブロックされるため、そうだと思います。

ジェネレーターが非同期タスク処理を行うとは思いません。ジェネレーターでは、同時に実行されているのは1つだけです。つまり、1つの関数が実行を停止し、別の関数に制御を渡すことができるということです。例えば、

function iofunc1() {
  console.log('iofunc1');
}

function iofunc2() {
  console.log('iofunc2');
}

function* do_stuff() {
  yield iofunc1;
  yield iofunc2;
  console.log('goodbye');
}


var gen = do_stuff();
(gen.next().value)(); 
(gen.next().value)(); //This line won't begin execution until the function call on the previous line returns
gen.next(); //continue executing do_stuff

Nodejsジェネレーターに関するいくつかの記事を読んだ場合:

  1. http://jlong​​ster.com/2012/10/05/javascript-yield.html
  2. http://jlong​​ster.com/A-Study-on-Solving-Callbacks-with-JavaScript-Generators
  3. http://jlong​​ster.com/A-Closer-Look-at-Generators-Without-Promises

...それらはすべて、非同期実行を追加するために追加の関数/ライブラリを採用しています。

4
7stud

1:ジェネレーターを使用して「線形」非同期コードを作成するには、ジェネレーターを反復処理し、生成された関数をコールバックとして実行し、コールバックの結果をジェネレーターに返す呼び出し関数が必要ですか?

はい。それを「ランチャー」と呼びましょう。

2:質問1の答えが「はい」の場合、生成された関数は正確にどのように実行されますか?非同期ですか?

ジェネレーター内で、関数とそのパラメーターを含む配列を生成します。制御する呼び出し元(ランチャー)では、fn.apply(..、callback)を使用して非同期を呼び出し、呼び出しを「generator.next(data);」に置きます。 (再開)コールバック内。

async関数は非同期で実行されますが、コールバックが呼び出されるまで(その後、「generator.next(data)」が実行されるまで)、ジェネレーターはyieldポイントで「一時停止」されます。

完全に機能するライブラリとサンプル: https://github.com/luciotato/waitfor-es6

2
Lucio M. Tato