Faser/Faser
Im Betriebssystem gibt es neben Prozessen und Threads auch eine Art Faser (Faser, auch Coroutine genannt), die selten verwendet wird. Fasern werden oft mit Threads verglichen, da es sich bei beiden um leichtgewichtige Betriebszustände handelt. Fasern gelten im Allgemeinen als leichter und haben weniger Overhead als Fäden. Der Unterschied besteht darin, dass Fasern durch Threads oder Fasern erstellt werden und die Faserplanung vollständig durch den Systemkernel gesteuert wird. Fasern realisieren kooperatives Multitasking den Kernel und implementieren präventives Multitasking entsprechend der Priorität. Darüber hinaus kennt der Systemkernel den spezifischen Betriebsstatus von Fasern nicht und die Verwendung von Fasern ist tatsächlich relativ unabhängig vom Betriebssystem.
Im Knoten ist ein einzelner Thread nur für Javascript vorgesehen, und die unterste Ebene ist tatsächlich voller Multithreads. Wenn Sie Multithreading in JavaScript implementieren müssen, besteht ein gängiger Ansatz darin, ein C-Add-on zu schreiben, um den Single-Thread-Mechanismus von JavaScript zu umgehen. Diese Methode erhöht jedoch die Schwierigkeit und die Kosten für die Entwicklung und das Debuggen. Wie viele andere Skriptsprachen können wir auch das Konzept der Fasern in Node einführen.
Knotenfasern
Die Knotenfaserbibliothek stellt Faserfunktionen für Knoten bereit. Der Multithreading-Test hat nicht zu optimalen Ergebnissen geführt, hat jedoch erhebliche Auswirkungen auf die Konvertierung von asynchronem in synchrones Verhalten und kann auch bei der Reduzierung von Knotenaufrufstapeln und unendlicher Rekursion von Nutzen sein. In diesem Dokument wird hauptsächlich die Verwendung der Node-Fibers-Bibliothek und die Konvertierung von Asynchron zu Synchron vorgestellt.
Installieren
Knotenfasern sind in der Sprache C geschrieben. Das direkte Herunterladen des Quellcodes erfordert eine Kompilierung. Normalerweise können Sie ihn direkt mit npm installieren:
Nutzung der Faserbibliothek
API
1.Faser(fn)/ neue Faser(fn):
Erstellen Sie eine Faser, die als Konstruktor verwendet oder als gewöhnliche Funktion aufgerufen werden kann. Zum Beispiel:
Wenn run() aufgerufen wird, startet die Fiber und weist einen neuen Stack für fn zu. fn wird auf diesem neuen Stack ausgeführt, bis fn einen Rückgabewert hat oder yield() aufgerufen wird. Nachdem fn yield() zurückgegeben oder aufgerufen hat, wird der Stack zurückgesetzt. Wenn run() erneut aufgerufen wird, wird die Fiber erneut gestartet und fn wird zum ersten Mal im zugewiesenen Stack ausgeführt.
2.Faserstrom:
Besorgen Sie sich die aktuelle Glasfaser und betreiben Sie sie. Wenn Sie eine Variable angeben, die damit verknüpft werden soll, stellen Sie sicher, dass diese Faser freigegeben werden kann. Andernfalls ignoriert der Garbage-Collection-Mechanismus von V8 diesen Teil des Speichers immer und führt zu Speicherverlusten.
3.Fiber.yield(param):
Diese Funktion wurde in der vorherigen Beschreibung erwähnt. Die Methode yield () wird verwendet, um die Faser zu unterbrechen, ähnlich wie bei einer Rückkehr bis zu einem gewissen Grad. Sobald yield() ausgeführt wird, kann nachfolgender Code in dieser Fiber nicht mehr ausgeführt werden, zum Beispiel:
Nach der Ausführung wird nur „Fiber Start“ ausgegeben und der letztere Ausgabebefehl wird nicht ausgeführt. Wird an yield() ein Parameter übergeben, so wird dieser Parameter als Rückgabewert von run() verwendet.
4.Fiber.prototype.run(param):
Diese Methode ist bereits sehr bekannt. Es gibt zwei Zeitformen für den Aufruf von run(), eine ist, wenn die Faser nicht gestartet wird, und die andere ist, wenn die Faser nachgegeben wird. Das Verhalten von run() ist in diesen beiden Zeitformen nicht dasselbe.
Wenn Fiber nicht gestartet ist, akzeptiert run() ein Argument und übergibt es als Argument an fn. Wenn Fiber den Yield-Status verarbeitet, akzeptiert run() einen Parameter und verwendet ihn als Rückgabewert von yield(). Fn wird nicht von Anfang an ausgeführt, sondern ab dem Punkt der Unterbrechung weiter ausgeführt. Die Beziehung zwischen den Parametern und Rückgabewerten von fn, yield und run kann anhand des folgenden kleinen Beispiels erklärt werden:
Die Ausgabe lautet wie folgt:
Aus dem obigen Beispiel wird deutlich, dass sich die Verwendung von yield erheblich von der aktuellen JavaScript-Syntax unterscheidet. Das Schlüsselwort yield wurde in anderen Sprachen (C#, Python usw.) als Interrupt für Iteratoren implementiert. Sie können genauso gut einen Iterator auf dem Knoten implementieren und die Verwendung von yield im Detail erleben. Nehmen wir als Beispiel die Fibonacci-Folge am Anfang:
Die Ausgabe ist:
Es gibt zwei Dinge, auf die man achten sollte: Yield soll eine Methode sein, eher ein Schlüsselwort. Im Gegensatz zu run muss sich yield nicht auf eine Fiber-Instanz stützen. Wenn Sie run in Fiber aufrufen, müssen Sie Folgendes verwenden: Fiber.current.run(); zweitens ist yield selbst ein reserviertes Schlüsselwort von JavaScript. Es ist nicht sicher, ob und wann es aktiviert wird, daher kann es zu Änderungen am Code kommen Zukunft. .
5.Fiber.prototype.reset():
Wir wissen bereits, dass Fiber unterschiedliche Zeitformen haben kann, was sich auch auf das Laufverhalten auswirkt. Die Reset-Methode kehrt in den Ausgangszustand zurück, unabhängig davon, welchen Zustand Fiber verarbeitet. Bei der anschließenden Ausführung von „run“ wird fn erneut ausgeführt.
6.Fiber.prototype.throwInto(Exception):
Im Wesentlichen löst throwInto die an ihn übergebene Ausnahme aus und verwendet die Ausnahmeinformationen als Rückgabewert von run. Wenn die von ihr ausgelöste Ausnahme nicht in Fiber behandelt wird, wird die Ausnahme weiterhin in die Luft sprudeln. Unabhängig davon, ob die Ausnahme behandelt wird oder nicht, erzwingt sie eine Ausbeute und unterbricht Fiber.
Nutzung der zukünftigen Bibliothek
Es ist nicht immer sinnvoll, Fiber direkt im Knoten zu verwenden, da die API von Fiber sehr einfach ist. Bei der tatsächlichen Verwendung wird zwangsläufig sich wiederholender und langwieriger Code erzeugt, was der Wartung nicht förderlich ist. Es wird empfohlen, eine Abstraktionsebene zwischen Knoten und Fiber hinzuzufügen, damit Fiber besser funktioniert. Die zukünftige Bibliothek bietet eine solche Abstraktion. Die zukünftige Bibliothek oder irgendeine Abstraktionsebene ist möglicherweise nicht perfekt, sondern nur das, was anwendbar ist oder nicht. Beispielsweise stellt uns die zukünftige Bibliothek eine einfache API zur Verfügung, die die Arbeit von asynchron zu synchron abschließen kann, aber nichts zur Kapselung des Generators tun kann (ähnlich dem oben genannten Fibonacci-Sequenzgenerator).
Die Future-Bibliothek muss nicht separat heruntergeladen und installiert werden. Sie ist bereits in der Fibers-Bibliothek enthalten. Wenn Sie sie verwenden, benötigen Sie nur var future=require('fibers/future').
API
1.Function.prototype.future():
Future-Methode zum Funktionstyp hinzugefügt, um die Funktion in eine „Future-Funktion“ umzuwandeln.
Eigentlich wird die Power-Methode in Fibel ausgeführt. Die vorhandene Version von Future weist jedoch Fehler auf und es gibt keine offizielle offizielle Erklärung. Wenn Sie diese Funktion verwenden müssen, löschen Sie bitte die Zeilen 339 und 350 von Future.js.
2.neue Zukunft()
Konstruktor des Future-Objekts, siehe unten.
3.Future.wrap(fn, idx)
Die Wrap-Methode kapselt den asynchronen in den synchronen Betrieb und ist für uns die wertvollste Methode in der zukünftigen Bibliothek. fn stellt die Funktion dar, die konvertiert werden muss, idx stellt die Anzahl der von fn akzeptierten Parameter dar und seine Rückrufmethode wird als letzter Parameter betrachtet (die Formulierung der API ist hier ziemlich umstritten. Einige Leute neigen dazu, die Position wo zu übergeben Glücklicherweise ist die Wrap-Methode relativ einfach und kann einfacher geändert werden. Sie können die Verwendung von Wrap anhand eines Beispiels verstehen:
Aus diesem Beispiel können wir ersehen, dass die Konvertierung von asynchroner zu synchroner Glasfaser tatsächlich sehr effektiv ist. Bis auf den zusätzlichen Schritt .wait() in der Syntax sind die anderen bereits von fs bereitgestellten fs.readFileSync-Methoden dieselben.
4.Future.wait(Futures):
Diese Methode wurde schon oft gesehen. Wie der Name schon sagt, besteht seine Funktion darin, auf Ergebnisse zu warten. Wenn Sie auf das Ergebnis einer zukünftigen Instanz warten möchten, rufen Sie einfach futureInstance.wait() direkt auf. Wenn Sie auf das Ergebnis einer Reihe zukünftiger Instanzen warten müssen, rufen Sie Future.wait(futuresArray) auf. Es ist zu beachten, dass bei der zweiten Verwendung die Wartemethode keinen Fehler auslöst, wenn beim Ausführen einer zukünftigen Instanz ein Fehler auftritt. Wir können jedoch die Methode get () verwenden, um das laufende Ergebnis direkt abzurufen.
5.Future.prototype.get():
Die Verwendung von get() ist der ersten Methode von wait() sehr ähnlich. Der Unterschied besteht darin, dass get() das Ergebnis sofort zurückgibt. Wenn die Daten nicht bereit sind, gibt get() einen Fehler aus.
6.Future.prototype.resolve(param1,param2):
Die obige Wrap-Methode vermittelt den Leuten immer den Eindruck, dass die Zukunft tatsächlich die Rückruffunktion der asynchronen Methode verschluckt und das asynchrone Ergebnis direkt zurückgibt. Tatsächlich bietet Future auch eine Lösung zum Festlegen von Rückruffunktionen über die Auflösungsmethode. „resolve“ akzeptiert bis zu zwei Parameter. Wenn nur ein Parameter übergeben wird, geht Future davon aus, dass eine Callback-Funktion im Node-Stil übergeben wird, wie im folgenden Beispiel:
Si deux paramètres sont transmis, cela signifie que l'erreur et les données seront traitées séparément. L'exemple est le suivant :
De plus, future ne distingue pas le moment de l'appel de résolution. Si les données ne sont pas prêtes, la fonction de rappel sera placée dans la file d'attente et planifiée uniformément par la méthode solver(). Sinon, les données seront récupérées directement. et la fonction de rappel sera exécutée immédiatement.
7.Future.prototype.isResolved() :
Renvoie une valeur booléenne indiquant si l'opération a été effectuée.
8.Future.prototype.proxy(futureInstance) :
La méthode proxy fournit un proxy pour les instances futures, qui est essentiellement un wrapper pour la méthode de résolution. En fait, elle utilise la méthode de rappel d'une instance comme exécuteur de rappel d'une autre instance. Par exemple :
Bien que le proxy soit exécuté, la fonction de rappel de la cible est finalement exécutée et le résultat de l'exécution du proxy pilote la fonction de rappel de la cible. Cette méthode proxy peut jouer un rôle important dans nos applications pratiques, mais je n'y ai pas encore réfléchi en profondeur.
9.Future.prototype.return(value):
10.Future.prototype.throw(erreur) :
11.Future.prototype.resolver() :
12.Future.prototype.detach() :
En ce qui concerne les quatre API ci-dessus, j'estime que par rapport à d'autres API, les scénarios ou fonctions d'utilisation réels sont relativement moyens. Le retour et le lancement sont planifiés par la méthode du résolveur. Ces trois méthodes sont très importantes et fonctionnent silencieusement dans le processus d'utilisation futur normal. Cependant, je n'ai pas trouvé de scénario spécifique pour les utiliser séparément, il n'y a donc aucun moyen de les introduire. en détails. La méthode detach ne peut être considérée que comme une version simplifiée de la méthode de résolution, et il n'est pas nécessaire de l'introduire.