Asynchroner Lebenszyklus von AsyncHooks in Node8

Asynchroner Lebenszyklus von AsyncHooks in Node8

Async Hooks ist eine neue Funktion von Node8. Sie bietet einige APIs zur Verfolgung des Lebenszyklus asynchroner Ressourcen in NodeJs. Es ist ein integriertes Modul von NodeJs und kann direkt referenziert werden.

const async_hooks = erfordern('async_hooks');

Dies ist ein selten verwendetes Modul. Warum existiert es?

Wir alle wissen, dass JavaScript von Anfang an als Single-Thread-Sprache konzipiert wurde. Dies hängt mit seiner ursprünglichen Designabsicht zusammen. Das ursprüngliche JavaScript wurde nur verwendet, um Formularüberprüfungen auf der Seite durchzuführen, um den Zeitaufwand der Benutzer zu reduzieren, die in Zeiten niedriger Netzwerkgeschwindigkeit auf Serverantworten warten. Obwohl die Front-End-Funktionen mit der Entwicklung der Web-Frontend-Technologie immer leistungsfähiger und wichtiger werden, scheint es kein Problem zu geben, das Single-Threading nicht lösen kann. Im Vergleich dazu scheint Multi-Threading komplizierter zu sein, sodass Single-Threading auch heute noch verwendet wird.

Da JavaScript ein Single-Thread ist, gibt es bei der täglichen Entwicklung immer einige zeitaufwändige Aufgaben, wie Timer und das mittlerweile standardisierte Ajax. Um diese Probleme zu lösen, unterteilt sich JavaScript in BOM, DOM und ECMAScript. BOM hilft uns bei der Lösung dieser zeitaufwändigen Aufgaben, die als asynchrone Aufgaben bezeichnet werden.

Da uns die BOM des Browsers bei der Verarbeitung asynchroner Aufgaben hilft, wissen die meisten Programmierer fast nichts über asynchrone Aufgaben, außer wie man sie verwendet. Wie viele asynchrone Aufgaben befinden sich beispielsweise gleichzeitig in der Warteschlange? Wir haben keine Möglichkeit, relevante Informationen direkt abzurufen, etwa ob der asynchrone Prozess überlastet ist. In vielen Fällen erfordert die zugrunde liegende Schicht nicht, dass wir auf relevante Informationen achten. Wenn wir jedoch in einigen Fällen relevante Informationen benötigen, stellt NodeJS eine experimentelle API zur Verfügung, die wir verwenden können: async_hooks. Warum NodeJS? Weil nur in Node asynchrone Module wie Timer und http von Entwicklern gesteuert werden können. Das BOM im Browser wird nicht von Entwicklern gesteuert, es sei denn, der Browser stellt die entsprechende API bereit.

async_hooks-Regeln

async_hooks legt fest, dass jede Funktion einen Kontext bereitstellt, den wir als asynchronen Bereich bezeichnen. Jeder asynchrone Bereich hat eine asyncId, die das Logo des aktuellen asynchronen Bereichs ist. Die asyncId im selben asynchronen Bereich muss dieselbe sein.

Wenn mehrere asynchrone Aufgaben parallel ausgeführt werden, können wir mithilfe von asyncId unterscheiden, welche asynchrone Aufgabe überwacht werden soll.

asyncId ist eine selbsterhöhende, sich nicht wiederholende positive Ganzzahl. Die erste asyncId eines Programms muss 1 sein.

Einfach ausgedrückt ist der asynchrone Bereich eine synchrone Aufgabe, die nicht unterbrochen werden kann. Solange sie nicht unterbrochen werden kann, wird unabhängig von der Länge des Codes eine asynchrone ID gemeinsam genutzt. Wenn sie jedoch in der Mitte unterbrochen werden kann, z. B. durch einen Rückruf oder ein Warten in der Mitte, wird ein neuer asynchroner Kontext erstellt und eine neue asynchrone ID wird erstellt.

Jeder asynchrone Bereich verfügt über eine „triggerAsyncId“, die angibt, dass die aktuelle Funktion durch diesen asynchronen Bereich ausgelöst wird.

Über asyncId und triggerAsyncId können wir die gesamte asynchrone Anrufbeziehung und -verknüpfung problemlos verfolgen.

async_hooks.executionAsyncId() wird verwendet, um die asyncId abzurufen. Sie können sehen, dass die globale asyncId 1 ist.

async_hooks.triggerAsyncId() wird verwendet, um triggerAsyncId zu erhalten, und sein aktueller Wert ist 0.

const async_hooks = erfordern('async_hooks');
console.log('asyncId:', async_hooks.executionAsyncId()); // asyncId: 1
console.log('triggerAsyncId:', async_hooks.triggerAsyncId()); // triggerAsyncId: 0

Wir verwenden fs.open, um hier eine Datei zu öffnen. Wir können feststellen, dass die asyncId von fs.open 7 ist und die triggerAsyncId von fs.open 1 wird. Dies liegt daran, dass fs.open durch einen globalen Aufruf ausgelöst wird und die globale asyncId 1 ist.

const async_hooks = erfordern('async_hooks');
console.log('asyncId:', async_hooks.executionAsyncId()); // asyncId: 1
console.log('triggerAsyncId:', async_hooks.triggerAsyncId()); // triggerAsyncId: 0
const fs = erfordern('fs');
fs.open('./test.js', 'r', (err, fd) => {
    console.log('fs.open.asyncId:', async_hooks.executionAsyncId()); // 7
    console.log('fs.open.triggerAsyncId:', async_hooks.triggerAsyncId()); // 1
});

Der Lebenszyklus einer asynchronen Funktion

Natürlich wird async_hooks in tatsächlichen Anwendungen nicht auf diese Weise verwendet. Die korrekte Verwendung besteht darin, Rückrufe vor, nach und nach dem Erstellen, Ausführen und Löschen aller asynchronen Aufgaben auszulösen, und alle Rückrufe werden in asyncId übergeben.

Wir können async_hooks.createHook verwenden, um einen asynchronen Ressourcen-Hook zu erstellen, der ein Objekt als Parameter empfängt, um einige Rückruffunktionen für Ereignisse zu registrieren, die im Lebenszyklus asynchroner Ressourcen auftreten können. Diese Hook-Funktionen werden jedes Mal ausgelöst, wenn eine asynchrone Ressource erstellt/ausgeführt/zerstört wird.

const async_hooks = erfordern('async_hooks');

const asyncHook = async_hooks.createHook({
  init(asyncId, Typ, triggerAsyncId, Ressource) { },
  zerstören(asyncId) { }
})

Derzeit kann die Funktion createHook die folgenden fünf Typen von Hook-Callbacks akzeptieren:

1. init(asyncId, Typ, triggerAsyncId, Ressource)

  • Die Init-Callbackfunktion wird normalerweise ausgelöst, wenn asynchrone Ressourcen initialisiert werden.
  • asyncId: Jede asynchrone Ressource generiert eine eindeutige Kennung
  • Typ: Der Typ der asynchronen Ressource, normalerweise der Name des Konstruktors der Ressource.

FSEVENTWRAP, FSREQCALLBACK, GETADDRINFOREQWRAP, GETNAMEINFOREQWRAP, HTTPINCOMINGMESSAGE,
HTTPCLIENTREQUEST, JSSTREAM, PIPECONNECTWRAP, PIPEWRAP, PROCESSWRAP, QUERYWRAP,
SHUTDOWNWRAP, SIGNALWRAP, STATWATCHER, TCPCONNECTWRAP, TCPSERVERWRAP, TCPWRAP,
TTYWRAP, UDPSENDWRAP, UDPWRAP, WRITEWRAP, ZLIB, SSLCONNECTION, PBKDF2REQUEST,
RANDOMBYTESREQUEST, TLSWRAP, Mikrotask, Zeitüberschreitung, Sofort, TickObject

  • triggerAsyncId: Gibt die asyncId des entsprechenden asynchronen Bereichs an, der die Erstellung der aktuellen asynchronen Ressource auslöst.
  • Ressource: stellt das zu initialisierende asynchrone Ressourcenobjekt dar

Wir können die Funktion async_hooks.createHook verwenden, um Listener-Funktionen für init/before/after/destory/promiseResolve und andere verwandte Ereignisse zu registrieren, die im Lebenszyklus jeder asynchronen Ressource auftreten.
Derselbe asynchrone Bereich kann mehrmals aufgerufen und ausgeführt werden. Unabhängig davon, wie oft er ausgeführt wird, muss seine asynchrone ID dieselbe sein. Durch die Überwachungsfunktion können wir die Anzahl und den Zeitpunkt seiner Ausführung sowie die Beziehung zum Online-Kontext problemlos verfolgen.

2. vorher(asyncId)

Die Funktion „Before“ wird im Allgemeinen aufgerufen, nachdem der asynchrone Ressourcenvorgang, der asyncId entspricht, abgeschlossen ist und bevor der Rückruf ausgeführt wird. Die Funktion „Before“ kann mehrmals ausgeführt werden, was durch die Anzahl der Rückrufe bestimmt wird. Bitte beachten Sie dies bei der Verwendung.

3.nach(asyncId)

Die After-Callback-Funktion wird im Allgemeinen unmittelbar aufgerufen, nachdem die asynchrone Ressource die Callback-Funktion ausgeführt hat. Wenn während der Ausführung der Callback-Funktion eine nicht abgefangene Ausnahme auftritt, wird das After-Ereignis aufgerufen, nachdem das Ereignis „uncaughtException“ ausgelöst wurde.

4.zerstören(asynchroneID)

Wird aufgerufen, wenn die asynchrone Ressource, die asyncId entspricht, zerstört wird. Die Zerstörung einiger asynchroner Ressourcen hängt vom Garbage Collection-Mechanismus ab. In einigen Fällen wird das Zerstörungsereignis aufgrund von Speicherlecks möglicherweise nie ausgelöst.

5. VersprechenResolve(asyncId)

Wenn die Resolve-Funktion im Promise-Konstruktor ausgeführt wird, wird das PromiseResolve-Ereignis ausgelöst. In einigen Fällen werden einige Resolve-Funktionen implizit ausgeführt. Beispielsweise gibt die Funktion .then ein neues Promise zurück, das zu diesem Zeitpunkt ebenfalls aufgerufen wird.

const async_hooks = erfordern('async_hooks');

// Holen Sie sich die asyncId des aktuellen Ausführungskontexts
const eid = async_hooks.executionAsyncId();

// Holen Sie sich die asyncId, die die aktuelle Funktion auslöst
const tid = async_hooks.triggerAsyncId();

// Erstellen Sie eine neue AsyncHook-Instanz. Alle diese Rückrufe sind optional const asyncHook =
    async_hooks.createHook({ init, vorher, nachher, zerstören, promiseResolve });

//AsyncHook.enable() muss zur Ausführung deklariert werden;

// Deaktivieren Sie das Abhören neuer asynchroner Ereignisse.
asyncHook.disable();

Funktion init(asyncId, Typ, triggerAsyncId, Ressource) { }

Funktion vor (asyncId) { }

Funktion nach (asyncId) { }

Funktion zerstören(asyncId) { }

Funktion promiseResolve(asyncId) { }

Versprechen

Promise ist ein Sonderfall. Wenn Sie genau genug hinschauen, werden Sie feststellen, dass im Typ der Init-Methode kein PROMISE vorhanden ist. Wenn Sie nur ah.executionAsyncId() verwenden, um die asyncId von Promise abzurufen, können Sie nicht die richtige ID erhalten. Erst nach dem Hinzufügen des eigentlichen Hooks erstellt async_hooks eine asyncId für den Promise-Rückruf.

Mit anderen Worten: Da V8 hohe Ausführungskosten für das Abrufen von asyncId verursacht, weisen wir Promise standardmäßig keine neue asyncId zu.
Das heißt, wenn wir Promises oder async/await verwenden, können wir standardmäßig nicht die richtige asyncId und triggerId des aktuellen Kontexts abrufen. Aber das spielt keine Rolle, wir können die Zuweisung von asyncId zu Promise erzwingen, indem wir die Funktion async_hooks.createHook(callbacks).enable() ausführen.

const async_hooks = erfordern('async_hooks');

const asyncHook = async_hooks.createHook({
  init(asyncId, Typ, triggerAsyncId, Ressource) { },
  zerstören(asyncId) { }
})
asyncHook.enable();

Versprechen.resolve(123).then(() => {
  console.log(`asyncId ${async_hooks.executionAsyncId()} triggerId ${async_hooks.triggerAsyncId()}`);
});

Darüber hinaus löst Promise nur die Hook-Ereignisfunktionen „init“ und „promiseResolve“ aus, und die Hook-Funktionen der Ereignisse „vorher“ und „nachher“ werden nur ausgelöst, wenn Promise verkettet ist, d. h. sie werden nur ausgelöst, wenn Promise in der Funktion .then/.catch generiert wird.

neues Versprechen(lösen => {
    Entschlossenheit (123);
}).dann(Daten => {
    konsole.log(Daten);
})

Es ist ersichtlich, dass es oben zwei Promises gibt, das erste wird durch eine neue Instanziierung erstellt und das zweite wird bis dahin erstellt (wenn Sie es nicht verstehen, können Sie den vorherigen Artikel zum Promise-Quellcode lesen).

Die Reihenfolge ist hier, dass beim Ausführen eines neuen Promise seine eigene Init-Funktion aufgerufen wird und dann beim Auflösen die Funktion promiseResolve aufgerufen wird. Führen Sie dann die Init-Funktion des zweiten Promise in der Then-Methode aus und führen Sie dann die Funktionen Before, PromiseResovle und After des zweiten Promise aus.

Ausnahmebehandlung

Wenn in der registrierten Async-Hook-Callback-Funktion eine Ausnahme auftritt, druckt der Dienst ein Fehlerprotokoll und wird sofort beendet. Gleichzeitig werden alle Listener entfernt und das Ereignis „exit“ ausgelöst, um das Programm zu beenden.

Der Grund, warum der Prozess sofort beendet wird, ist, dass, wenn diese asynchronen Hook-Funktionen instabil laufen, wahrscheinlich eine Ausnahme ausgelöst wird, wenn das nächste gleiche Ereignis ausgelöst wird. Diese Funktionen werden hauptsächlich zur Überwachung asynchroner Ereignisse verwendet. Wenn sie instabil sind, sollten sie rechtzeitig entdeckt und korrigiert werden.

Drucken von Protokollen in asynchronen Hook-Rückrufen

Da die Funktion console.log ebenfalls ein asynchroner Aufruf ist, wird das entsprechende Hook-Ereignis erneut ausgelöst, wenn wir console.log in der asynchronen Hook-Funktion erneut aufrufen, was zu einem Aufruf in einer Endlosschleife führt. Daher müssen wir synchrones Logging verwenden, um die asynchrone Hook-Funktion zu verfolgen. Wir können die Funktion fs.writeSync verwenden:

const fs = erfordern('fs');
const util = erfordern('util');

Funktion debug(...args) {
  fs.writeFileSync('log.out', `${util.format(...args)}\n`, { flag: 'a' });
}

[Referenzen - AsyncHooks] (https://nodejs.org/dist/latest-v15.x/docs/api/async_hooks.html)

Dies ist das Ende dieses Artikels über den asynchronen Lebenszyklus von AsyncHooks in Node8. Weitere relevante Inhalte zum asynchronen Lebenszyklus von Node AsyncHooks finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder durchsuchen Sie die verwandten Artikel weiter unten. Ich hoffe, Sie werden 123WORDPRESS.COM auch in Zukunft unterstützen!

Das könnte Sie auch interessieren:
  • Verwendung des Node.js-HTTP-Moduls
  • Nodejs Exploration: Tiefgreifendes Verständnis des Prinzips der Single-Threaded High Concurrency
  • Es ist ganz einfach zu verstehen, was Node.js ist
  • Spezifische Verwendung globaler Variablen von node.js
  • Nodejs-Fehlerbehandlungsprozessaufzeichnung
  • Der gesamte Prozess der Verwendung von node.js Express zum automatischen Erstellen des Projekts
  • So verwenden Sie Shell-Skripte in Node
  • Der Kernprozess der NodeJS-Verarbeitung einer TCP-Verbindung
  • Detaillierte Erklärung der Nodejs-Array-Warteschlange und der forEach-Anwendung
  • Vergleich zwischen Node.js und Deno

<<:  Alibaba Cloud Centos7 Installation und Konfiguration von SVN

>>:  mysql5.7 Installations- und Konfigurationstutorial unter Centos7.3

Artikel empfehlen

Durchführung der lokalen Migration von Docker-Images

Ich habe vor Kurzem Docker gelernt und stoße dabe...

Detaillierte Erläuterung des MySQL-Download- und Installationsprozesses

1: MySql herunterladen Offizielle Download-Adress...

Detaillierte Erklärung zum effizienten Importieren mehrerer SQL-Dateien in MySQL

MySQL bietet mehrere Möglichkeiten, mehrere SQL-D...

Die Grundprinzipien und die detaillierte Verwendung des Ansichtsfensters

1. Übersicht über das Ansichtsfenster Mobile Brow...

Erstellen von Befehlszeilen-Anwendungen mit JavaScript

Inhaltsverzeichnis 1. Knoten installieren 2. Inst...

Vue implementiert Baumtabelle

In diesem Artikelbeispiel wird der spezifische Co...

Vue verwendet Monaco, um Codehervorhebung zu erreichen

Mithilfe der Vue-Sprache und Elementkomponenten m...

Nginx tp3.2.3 404 Problemlösung

Vor Kurzem habe ich Apache auf nginx umgestellt. ...

Wann ist die Verwendung von dl, dt und dd sinnvoll?

dl: Definitionsliste Definitionsliste dt: Definiti...

So aktualisieren Sie die Ansicht synchron nach Datenänderungen in Vue

Vorwort Vor kurzem bin ich auf ein interessantes ...

Beispiel für die MySQL-Volltext-Fuzzy-Suche nach der Methode MATCH AGAINST

MySQL 4.x und höher bieten Unterstützung für die ...