Lernen Sie die asynchrone Programmierung in Node.js in einem Artikel

Lernen Sie die asynchrone Programmierung in Node.js in einem Artikel

Inhaltsverzeichnis Einführung Synchron Asynchron und Blockierend Nicht blockierend Callbacks in JavaScript Fehlerbehandlung von Callback-Funktionen Callback-Hölle Promise in ES6 Was ist Promise? Funktionen von Promise Vorteile von Promise Nachteile von Promise Verwendung von Promise Ausführungsreihenfolge von Promise Ausführungsreihenfolge von async und await async Zusammenfassung der Funktionen von async

Einführung

Weil JavaScript standardmäßig ein Thread ist, was bedeutet, dass der Code keine neuen Threads erstellen kann, um sie parallel auszuführen. Für das JavaScript, das zuerst im Browser ausgeführt wurde, konnte die Umgebung für die synchrone Ausführung mit einem Thread die Anforderungen benutzerreaktiver Funktionen wie Seitenklicks und Mausbewegungen offensichtlich nicht erfüllen. Der Browser hat daher eine Reihe von APIs implementiert, die es JavaScript ermöglichen, im Callback-Verfahren asynchron auf Seitenanforderungsereignisse zu reagieren.

Darüber hinaus führte NodeJS nicht blockierende E/A ein und erweiterte so das Konzept der Asynchronität auf Dateizugriffe, Netzwerkaufrufe usw.

Heute werden wir einen detaillierten Blick auf die Vor- und Nachteile sowie die Entwicklungstrends verschiedener asynchroner Programmierungen werfen.

Synchron, asynchron, blockierend, nicht blockierend

Bevor wir die asynchrone Programmierung von node.js besprechen, besprechen wir ein Konzept, das leichter zu verwechseln ist, nämlich synchron, asynchron, blockierend und nicht blockierend.

Das sogenannte Blockieren und Nichtblockieren bezieht sich darauf, ob ein Prozess oder Thread beim Ausführen von Vorgängen oder beim Lesen und Schreiben von Daten warten muss und ob während des Wartevorgangs andere Vorgänge ausgeführt werden können.

Wenn ein Warten erforderlich ist und der Thread oder Prozess während des Wartevorgangs keine anderen Vorgänge ausführen kann und nur dumm warten kann, dann sagen wir, dass dieser Vorgang blockiert ist.

Im Gegenteil: Wenn ein Prozess oder Thread während der Ausführung einer Operation oder beim Lesen und Schreiben von Daten andere Operationen ausführen kann, dann bezeichnen wir die Operation als nicht blockierend.

Synchron und asynchron beziehen sich auf die Art des Datenzugriffs. Synchron bedeutet, dass Daten aktiv gelesen werden müssen und dieser Lesevorgang blockierend oder nicht blockierend sein kann. Asynchron bedeutet, dass kein aktives Auslesen der Daten notwendig ist, es handelt sich um eine passive Benachrichtigung.

Offensichtlich ist der Rückruf in JavaScript eine passive Benachrichtigung, die wir einen asynchronen Aufruf nennen können.

Rückrufe in Javascript

Callbacks in JavaScript sind ein sehr typisches Beispiel für asynchrone Programmierung:

document.getElementById('button').addEventListener('click', () => {
 console.log('Schaltfläche angeklickt!');
})

Im obigen Code haben wir der Schaltfläche einen Klickereignis-Listener hinzugefügt. Wenn ein Klickereignis abgehört wird, wird die Rückruffunktion ausgelöst, um die entsprechenden Informationen auszugeben.

Die Rückruffunktion ist eine normale Funktion, mit dem Unterschied, dass sie als Parameter an addEventListener übergeben wird und nur aufgerufen wird, wenn das Ereignis ausgelöst wird.

Die setTimeout- und setInterval-Funktionen, über die wir im vorherigen Artikel gesprochen haben, sind eigentlich asynchrone Rückruffunktionen.

Fehlerbehandlung in Callback-Funktionen

Wie gehe ich mit Rückruffehlerinformationen in Node.js um? Nodejs verwendet eine sehr clevere Methode. In Nodejs ist der erste Parameter in jeder Rückruffunktion das Fehlerobjekt. Wir können eine entsprechende Fehlerbehandlung durchführen, indem wir beurteilen, ob dieses Fehlerobjekt existiert oder nicht.

fs.readFile('/Datei.json', (err, data) => {
 wenn (err !== null) {
 //Fehler behandeln console.log(err)
 zurückkehren
 }

 // Wenn kein Fehler vorliegt, verarbeite die Daten.
 console.log(Daten)
})

Rückruf Hölle

Obwohl JavaScript-Rückrufe hervorragend sind, lösen sie effektiv das Problem der synchronen Verarbeitung. Wenn wir uns jedoch zum Ausführen des nächsten Schritts auf den Rückgabewert der Rückruffunktion verlassen müssen, geraten wir leider in diese Rückrufhölle.

Es als Callback-Hölle zu bezeichnen ist zwar etwas übertrieben, spiegelt aber auch die Probleme von Callback-Funktionen aus einer Perspektive wider.

fs.readFile('/a.json', (err, data) => {
 wenn (err !== null) {
 fs.readFile('/b.json',(err,data) => {
  //Rückruf innerhalb des Rückrufs
 })
 }
})

Wie kann man das Problem lösen?

Keine Angst, ES6 hat Promise und ES2017 Async/Await eingeführt, was dieses Problem lösen kann.

Versprechen in ES6

Was ist ein Versprechen?

Promise ist eine Lösung für asynchrone Programmierung, die sinnvoller und leistungsfähiger ist als die herkömmliche Lösung von „Rückruffunktionen und Ereignissen“.

Das sogenannte Promise ist einfach ein Container, der das Ergebnis eines Ereignisses speichert, das in der Zukunft endet (normalerweise eine asynchrone Operation).

Syntaktisch gesehen ist ein Promise ein Objekt, von dem Sie Nachrichten über asynchrone Vorgänge erhalten können.

Funktionen von Promise

Promise hat zwei Eigenschaften:

Der Zustand des Objekts wird nicht von der Außenwelt beeinflusst.

Das Promise-Objekt stellt eine asynchrone Operation dar und hat drei Zustände: Ausstehend (in Bearbeitung), Gelöst (abgeschlossen, auch als Erfüllt bekannt) und Abgelehnt (fehlgeschlagen).

Nur das Ergebnis der asynchronen Operation kann den aktuellen Status bestimmen, und keine andere Operation kann diesen Status ändern.

Sobald sich der Status ändert, ändert er sich nicht mehr und das Ergebnis kann jederzeit abgerufen werden.

Es gibt nur zwei Möglichkeiten, den Status eines Promise-Objekts zu ändern: von „Ausstehend“ zu „Gelöst“ und von „Ausstehend“ zu „Abgelehnt“.

Dies ist etwas völlig anderes als ein Ereignis. Das Merkmal eines Ereignisses besteht darin, dass Sie keine Ergebnisse erzielen, wenn Sie es verpassen und dann darauf hören.

Vorteile von Promises

Promise drückt asynchrone Operationen in Form von synchronen Operationen aus und vermeidet verschachtelte Rückruffunktionen.

Das Promise-Objekt bietet eine einheitliche Schnittstelle, die die Steuerung asynchroner Vorgänge erleichtert.

Nachteile von Promises

  1. Das Versprechen kann nicht storniert werden. Sobald es erstellt wurde, wird es sofort ausgeführt und kann nicht auf halbem Weg storniert werden.
  2. Wenn die Rückruffunktion nicht festgelegt ist, wird der in Promise ausgelöste Fehler nicht extern angezeigt.
  3. Im Status „Ausstehend“ können Sie nicht wissen, in welchem ​​Stadium sich das Projekt derzeit befindet (gerade begonnen oder kurz vor dem Abschluss).

Verwendung von Promises

Das Promise-Objekt ist ein Konstruktor, der eine Promise-Instanz generiert:

var promise = neues Versprechen(Funktion(auflösen, ablehnen) { 
// ... etwas Code 
if (/* asynchroner Vorgang erfolgreich*/){ 
Auflösung (Wert); 
} sonst { ablehnen(Fehler); } 
}
);

Promise kann mit der then-Operation verbunden werden, und die then-Operation kann mit zwei Funktionsparametern verbunden werden. Der erste Funktionsparameter ist der aufgelöste Wert beim Erstellen des Promise und der zweite Funktionsparameter ist der Fehler bei der Ablehnung des Promise.

Versprechen.dann(Funktion(Wert) { 
// Erfolg 
}, Funktion(Fehler) { 
// Versagen }
);

Schauen wir uns ein konkretes Beispiel an:

Funktion Timeout (ms) {
 returniere neues Promise(((lösen, ablehnen) => {
  setTimeout(auflösen, ms, „erledigt“);
 }))
}

Timeout (100).dann (Wert => Konsole.log (Wert));

In Promise wird eine setTimeout-Methode aufgerufen, die zu einem festgelegten Zeitpunkt die Resolve-Methode auslöst und den Parameter done übergibt.

Schließlich gibt das Programm fertig aus.

Reihenfolge der Versprechensausführung

Sobald ein Promise erstellt wurde, wird es sofort ausgeführt. Die Methode in Promise.then wartet jedoch, bis ein Aufrufzyklus abgelaufen ist, bevor sie erneut aufruft. Sehen wir uns das folgende Beispiel an:

let promise = neues Versprechen(((lösen, ablehnen) => {
 console.log('Schritt1');
 lösen();
}));

versprechen.dann(() => {
 console.log('Schritt3');
});

console.log('Schritt2');

Ausgabe:
Schritt 1
Schritt 2
Schritt 3

asynchron und warten

Versprechen sind großartig, natürlich wandeln wir die Callback-Hölle in Kettenaufrufe um. Wir verwenden „then“, um mehrere Promises zu verbinden, und das Ergebnis der Auflösung des vorherigen Promises ist der Parameter von „then“ im nächsten Promise.

Welche Nachteile haben Kettenanrufe?

Wenn wir beispielsweise einen Wert aus einem Versprechen auflösen, müssen wir basierend auf diesem Wert einige Geschäftslogikverarbeitungen durchführen.

Wenn diese Geschäftslogik sehr lang ist, müssen wir im nächsten Schritt einen langen Geschäftslogikcode schreiben. Dadurch erscheint unser Code sehr redundant.

Gibt es also eine Möglichkeit, das Ergebnis der Lösung direkt im Versprechen zurückzugeben?

Die Antwort ist: abwarten.

Wenn einem Versprechen ein „Warten“ vorangestellt ist, wird der aufrufende Code angehalten, bis das Versprechen eingelöst oder abgelehnt wird.

Beachten Sie, dass „await“ in einer asynchronen Funktion platziert werden muss. Sehen wir uns ein Beispiel für „async“ und „await“ an:

const logAsync = () => {
 gib ein neues Versprechen zurück (Auflösen => {
 setTimeout(() => lösen('Xiao Ma Ge'), 5000)
 })
}

Oben haben wir eine logAsync-Funktion definiert, die ein Promise zurückgibt. Da das Promise zur Auflösung setTimeout verwendet, können wir es als asynchron betrachten.

Um „await“ zum Abrufen des Resolve-Werts zu verwenden, müssen wir ihn in eine asynchrone Funktion einfügen:

const doSomething = async () => {
 const resolveValue = warte auf logAsync();
 console.log(Wert auflösen);
}

Asynchrone Ausführungsreihenfolge

Await wartet tatsächlich auf das Auflösungsergebnis des Versprechens. Kombinieren wir die obigen Beispiele:

const logAsync = () => {
 gib ein neues Versprechen zurück (Auflösen => {
  setTimeout(() => lösen('Xiao Ma Ge'), 1000)
 })
}

const doSomething = async () => {
 const resolveValue = warte auf logAsync();
 console.log(Wert auflösen);
}

console.log('vorher')
tuEtwas();
console.log('nach')

Das obige Beispiel gibt Folgendes aus:

vor
nach
Kleine Mama

Wie Sie sehen, wird aysnc asynchron ausgeführt und seine Sequenz folgt dem aktuellen Zyklus.

Funktionen von async

Async wandelt alle nachfolgenden Funktionen in Promises um, auch wenn die nachfolgenden Funktionen nicht explizit Promises zurückgeben.

const asyncReturn = async () => {
 returniere 'asynchrone Rückgabe'
}

asyncReturn().then(Konsole.log)

Da bis dahin nur Promise befolgt werden kann, können wir sehen, dass async eine normale Funktion in ein Promise kapselt:

const asyncReturn = async () => {
 returniere Promise.resolve('asynchrone Rückgabe')
}

asyncReturn().then(Konsole.log)

Zusammenfassen

Promise vermeidet die Callback-Hölle, indem es den Callback innerhalb des Callbacks in einen Kettenaufruf von then umschreibt.

Kettenaufrufe sind jedoch zum Lesen und Debuggen nicht praktisch. So erschienen async und await.

Asynchrone und wartende Aufrufe ändern die Kettenaufrufe in eine Syntax, die der sequentiellen Ausführung von Programmen ähnelt, wodurch sie leichter zu verstehen und zu debuggen sind.

Schauen wir uns einen Vergleich an und betrachten zunächst die Verwendung von Promise:

const getUserInfo = () => {
 return fetch('/users.json') // Benutzerliste abrufen.then(response => response.json()) // JSON analysieren
 .then(users => users[0]) // Den ersten Benutzer auswählen.then(user => fetch(`/users/${user.name}`)) // Benutzerdaten abrufen.then(userResponse => userResponse.json()) // JSON analysieren
}

getUserInfo()

Schreiben Sie es mit async neu und warten Sie:

const getUserInfo = async () => {
 const response = await fetch('/users.json') // Benutzerliste abrufen const users = await response.json() // JSON analysieren
 const user = users[0] // Ersten Benutzer auswählen const userResponse = await fetch(`/users/${user.name}`) // Benutzerdaten abrufen const userData = await userResponse.json() // JSON analysieren
 Benutzerdaten zurückgeben
}

getUserInfo()

Sie sehen, dass die Geschäftslogik klarer wird. Gleichzeitig erhalten wir viele Zwischenwerte, was uns das Debuggen erleichtert.

Dies ist das Ende dieses Artikels über ein vertieftes Verständnis der asynchronen Programmierung in nodejs. Weitere relevante Inhalte zur asynchronen Programmierung in nodejs finden Sie in früheren Artikeln auf 123WORDPRESS.COM oder in den folgenden verwandten Artikeln. Ich hoffe, dass jeder 123WORDPRESS.COM in Zukunft unterstützen wird!

Das könnte Sie auch interessieren:
  • Detaillierte Erläuterung der asynchronen Prozessimplementierung in Single-Thread-JavaScript
  • Analysieren Sie die Eigenschaften des asynchronen IO-Rückrufs mit einem Thread in JS
  • Asynchrone Programmierung in Javascript: Verstehen Sie Promise wirklich?
  • Detaillierte Erläuterung der ersten Verwendung von Promise in der asynchronen JavaScript-Programmierung
  • JS-Prinzip der asynchronen Ausführung und Rückrufdetails
  • So schreiben Sie asynchrone Aufgaben in modernem JavaScript
  • Detaillierte Erklärung asynchroner Generatoren und asynchroner Iterationen in Node.js
  • Detaillierte Erläuterung der Wissenspunkte zur asynchronen Programmierung in nodejs
  • Eine kurze Diskussion über die drei Hauptprobleme von JS: Asynchronität und Single-Thread

<<:  Detaillierte Installations- und Konfigurationsschritte für die MySQL 5.7-Zip-Version (Zip-Version)

>>:  10 beliebte Windows-Apps, die auch unter Linux verfügbar sind

Artikel empfehlen

Beispielcode zur Implementierung eines 3D-Rotationseffekts mit reinem CSS

Verwenden Sie hauptsächlich die Eigenschaften „pr...

21 Best Practices zur MySQL-Standardisierung und -Optimierung!

Vorwort Jede gute Angewohnheit ist ein Schatz. Di...

Implementierung des Markdown-Renderings in einer Vue-Einzelseitenanwendung

Beim Rendern von Markdown habe ich zuvor den Vors...

IE8 verwendet den Multikompatibilitätsmodus, um Webseiten normal anzuzeigen

IE8 wird mehrere Kompatibilitätsmodi haben. Der IE...

Details zur Reihenfolge, in der MySQL my.cnf liest

Inhaltsverzeichnis Die Reihenfolge, in der MySQL ...

Detaillierte Erklärung zum Ändern des Standardports von nginx

Finden Sie zunächst heraus, wo sich die Konfigura...

React + ts realisiert den sekundären Verknüpfungseffekt

In diesem Artikel wird der spezifische Code von R...

Hinweise zum Proc-Dateisystem des Linux-Kernel-Gerätetreibers

/***************** * proc-Dateisystem************...

So legen Sie Verknüpfungssymbole in Linux fest

Vorwort Durch das Erstellen von Verknüpfungen in ...