繊維/繊維
オペレーティング システムには、プロセスとスレッドに加えて、めったに使用されない種類のファイバー (ファイバー、コルーチンとも呼ばれます) もあります。ファイバーは、オペレーティング システムにとってはどちらも軽量の実行状態と比較されることがよくあります。一般に、ファイバーはスレッドよりも軽量で、オーバーヘッドが少ないと考えられています。違いは、ファイバーがスレッドまたはファイバーによって作成され、ファイバーのスケジューリングがユーザー コードによって完全に制御されることです。ファイバーは、協調的なマルチタスクを実現します。カーネルを制御し、優先度に応じてプリエンプティブ マルチタスクを実装します。さらに、システム カーネルはファイバーの特定の実行ステータスを認識せず、ファイバーの使用は実際にはオペレーティング システムから比較的独立しています。
ノードでは、シングルスレッドは JavaScript 専用であり、その最下層は実際にはマルチスレッドでいっぱいです。 JavaScript でマルチスレッドを実装する必要がある場合、一般的なアプローチは、JavaScript のシングルスレッド メカニズムをバイパスする C アドオンを作成することです。ただし、この方法では開発とデバッグの困難さとコストが増加します。他の多くのスクリプト言語と同様に、ファイバーの概念をノードに導入することもできます。
ノードファイバー
ノードファイバーライブラリは、ノードのファイバー関数を提供します。マルチスレッド テストでは理想的な結果は得られませんでしたが、非同期から同期への変換には大きな効果があり、ノードのコール スタックと無限再帰を削減するのにも役立つ可能性があります。このドキュメントでは主に、node-fibers ライブラリの使用方法と非同期から同期への変換について紹介します。
インストール
ノードファイバーは C 言語で書かれています。ソース コードを直接ダウンロードするには、通常、npm を使用して直接インストールできます。
ファイバーライブラリの使用法
API
1.ファイバー(fn)/新しいファイバー(fn):
コンストラクターとして使用するか、通常の関数として呼び出すことができるファイバーを作成します。例:
run() が呼び出されると、ファイバーが開始され、fn に新しいスタックが割り当てられます。fn が戻り値を取得するか、yield() が呼び出されるまで、fn はこの新しいスタック上で実行されます。 fn が返されるか、yield() を呼び出した後、スタックはリセットされ、run() が再度呼び出されると、ファイバーが再び開始され、fn は初めて割り当てられたスタックで実行されます。
2.ファイバー電流:
現在のファイバーを取得して操作します。関連付けられる変数を指定する場合は、このファイバーが解放できることを必ず確認してください。解放しないと、V8 のガベージ コレクション メカニズムが常にメモリのこの部分を無視し、メモリ リークが発生します。
3.Fiber.yield(param):
この機能は前の説明で言及されています。 yield() メソッドは、ファイバーを中断して、ある程度まで戻るのと同様に使用されます。 yield() が実行されると、このファイバーの後続のコードは実行されなくなります。例:
実行後は「Fiber Start」のみが出力され、後者の出力コマンドは実行されません。パラメータが yield() に渡された場合、このパラメータは run() の戻り値として使用されます。
4.Fiber.prototype.run(param):
このメソッドはすでに非常によく知られており、前述した run() の呼び出しには 2 つの時制があり、1 つは Fiber が開始されていないとき、もう 1 つは Fiber が放棄されたときです。 run() の動作は、これら 2 つの時制では同じではありません。
Fiber が開始されていない場合、run() は引数を受け取り、それを引数として fn に渡します。 Fiber が降伏状態を処理するとき、run() はパラメーターを受け取り、それを yield() の戻り値として使用します。fn は最初から実行されませんが、中断された時点から実行を続けます。 fn、yield、run のパラメーターと戻り値の関係は、次の小さな例で説明できます:
出力は次のとおりです:
上記の例から、yield の使用法が現在の JavaScript 構文と大きく異なることは明らかです。 yield キーワードは、イテレータの割り込みとして他の言語 (C#、Python など) に実装されています。ノードにイテレーターを実装して、yield の使用法を詳しく体験してみるのもよいでしょう。例として冒頭のフィボナッチ数列を見てみましょう:
出力は次のとおりです:
注意すべき問題が 2 つあります。まず、yield はメソッドであり、run とは異なり、Fibre インスタンスに依存する必要はありません。 Fiber 内で run を呼び出す場合は、 Fiber.current.run(); を使用する必要があります。第 2 に、yield 自体は JavaScript の予約キーワードであるため、コードが変更される可能性があります。未来。 。
5.Fiber.prototype.reset():
Fiber にはさまざまな時制があり、それが run の動作にも影響することはすでにわかっています。リセット メソッドは、Fiber がどのような状態を処理していても、初期状態に戻ります。その後 run を実行すると、fn が再実行されます。
6.Fiber.prototype.throwInto(例外):
基本的に、 throwInto は渡された例外をスローし、例外情報を run の戻り値として使用します。スローされた例外が Fiber で処理されない場合、例外は発生し続けます。例外が処理されるかどうかに関係なく、強制的にイールドが発生し、Fibre が中断されます。
将来のライブラリの使用法
Fibre の API は非常に単純なので、実際に使用すると必然的に反復的で長いコードが生成され、メンテナンスに役立ちません。 Fiber がより適切に動作できるように、ノードと Fiber の間に抽象化レイヤーを追加することをお勧めします。 Future ライブラリは、そのような抽象化を提供します。将来のライブラリや抽象化のレベルは完璧ではない可能性があります。正しいか間違っているかはありません。適用できるかどうかだけです。たとえば、Future ライブラリは、非同期から同期への作業を完了できるシンプルな API を提供しますが、ジェネレーター (上記のフィボナッチ数列ジェネレーターと同様) をカプセル化することはできません。
future ライブラリを個別にダウンロードしてインストールする必要はありません。使用する場合は、var future=require('fibers/future') だけが必要です。
API
1.Function.prototype.future():
関数型に future メソッドを追加し、関数を「funture-function」に変換しました。
実際には、べき乗メソッドは Fibel 内で実行されます。ただし、future の既存のバージョンにはバグがあり、公式の説明はありません。この機能を使用する必要がある場合は、future.js の 339 行目と 350 行目を削除してください。
2.new Future()
Future オブジェクトのコンストラクター。詳細は以下で説明します。
3.Future.wrap(fn, idx)
wrap メソッドは、非同期から同期への操作をカプセル化するもので、将来のライブラリにおいて最も価値のあるメソッドです。 fn は変換する必要がある関数を表し、idx は fn によって受け入れられるパラメータの数を表し、そのコールバック メソッドは最後のパラメータとみなされます (ここでの API の定式化は非常に議論の余地があります。一部の人々は、次の位置を渡す傾向があります)幸いなことに、wrap メソッドは比較的単純なので、コードを変更するのが簡単です)。例を見ると、wrap の使用法を理解できます:
この例から、Fibre の非同期から同期への変換が実際に非常に効果的であることがわかります。構文内の追加ステップ .wait() を除けば、fs によって既に提供されている他の fs.readFileSync メソッドは同じです。
4.Future.wait(先物):
この方法はこれまでに何度も見られました。名前が示すように、その機能は結果を待つことです。将来のインスタンスの結果を待ちたい場合は、futureInstance.wait() を直接呼び出します。一連の将来のインスタンスの結果を待つ必要がある場合は、Future.wait(futuresArray) を呼び出します。 2 番目の使用法では、将来のインスタンスの実行中にエラーが発生した場合、wait メソッドはエラーをスローしませんが、get() メソッドを使用して実行結果を直接取得できることに注意してください。
5.Future.prototype.get():
get() の使用法は wait() の最初のメソッドと非常に似ていますが、get() は結果をすぐに返すという点が異なります。データの準備ができていない場合、get() はエラーをスローします。
6.Future.prototype.resolve(param1,param2):
上記の Wrap メソッドは常に、Future が実際に非同期メソッドのコールバック関数を飲み込んで、非同期の結果を直接返しているという印象を人々に与えます。実際、future は、resolve メソッドを通じてコールバック関数を設定するためのソリューションも提供します。 solve は最大 2 つのパラメーターを受け入れます。パラメーターが 1 つだけ渡された場合、future は次の例のようにノードスタイルのコールバック関数が渡されたとみなします。
If two parameters are passed in, it means that the error and data will be processed separately. The example is as follows:
In addition, future does not distinguish the calling timing of resolve. If the data is not ready, the callback function will be pushed into the queue and uniformly scheduled by the resolver() method. Otherwise, the data will be fetched directly and the callback function will be executed immediately.
7.Future.prototype.isResolved():
Returns a Boolean value indicating whether the operation has been performed.
8.Future.prototype.proxy(futureInstance):
The proxy method provides a proxy for future instances, which is essentially a wrapper for the resolve method. In fact, it uses the callback method of one instance as the callback executor of another instance. For example:
Although the proxy is executed, the callback function of the target is eventually executed, and the execution result of the proxy drives the callback function of the target. This proxy method may play a big role in our practical applications, but I haven't thought about it in depth yet.
9.Future.prototype.return(value):
10.Future.prototype.throw(error):
11.Future.prototype.resolver():
12.Future.prototype.detach():
As for the above four APIs, I feel that compared to other APIs, the actual usage scenarios or functions are relatively average. Both return and throw are scheduled by the resolver method. These three methods are very important and work silently in the normal future usage process. However, I have not figured out a specific scenario for using them separately, so there is no way to introduce them in detail. The detach method can only be regarded as a simplified version of the resolve method, and there is no need to introduce it.