Unlimited Plugins, WordPress themes, videos & courses! Unlimited asset downloads! From $16.50/m
Advertisement
  1. Code
  2. Web Development

Verwaltung der asynchronen Natur von Node.js.

by
Difficulty:IntermediateLength:LongLanguages:

German (Deutsch) translation by Alex Grigorovich (you can also view the original English article)

Mit Node.js können Sie schnell und einfach Apps erstellen. Aufgrund seiner asynchronen Natur kann es jedoch schwierig sein, lesbaren und verwaltbaren Code zu schreiben. In diesem Artikel zeige ich Ihnen einige Tipps, wie Sie dies erreichen können.


Callback Hell oder die Pyramide des Schicksals

Node.js ist so aufgebaut, dass Sie gezwungen sind, asynchrone Funktionen zu verwenden. Das bedeutet Rückrufe, Rückrufe und noch mehr Rückrufe. Sie haben wahrscheinlich selbst Code-Teile wie diesen gesehen oder sogar geschrieben:

Das ist ein Ausschnitt direkt aus einer meiner ersten Node.js-Apps. Wenn Sie in Node.js etwas Fortgeschritteneres getan haben, verstehen Sie wahrscheinlich alles, aber das Problem hier ist, dass sich der Code jedes Mal nach rechts bewegt, wenn Sie eine asynchrone Funktion verwenden. Es wird schwieriger zu lesen und schwieriger zu debuggen. Glücklicherweise gibt es einige Lösungen für dieses Durcheinander, sodass Sie die richtige für Ihr Projekt auswählen können.


Lösung 1: Rückrufbenennung und Modularisierung

Der einfachste Ansatz wäre, jeden Rückruf zu benennen (was Ihnen beim Debuggen des Codes hilft) und Ihren gesamten Code in Module aufzuteilen. Das obige Anmeldebeispiel kann in wenigen einfachen Schritten in ein Modul umgewandelt werden.

Die Struktur

Beginnen wir mit einer einfachen Modulstruktur. Um die obige Situation zu vermeiden, lassen Sie es, wenn Sie das Chaos nur in kleinere Chaos aufteilen, eine Klasse sein:

Die Klasse besteht aus zwei Parametern: username und password. Wenn wir uns den Beispielcode ansehen, benötigen wir drei Funktionen: eine, um zu überprüfen, ob der Benutzername korrekt ist (_checkUsername), eine, um das Kennwort zu überprüfen (_checkPassword) und eine weitere, um die benutzerbezogenen Daten (_getData) zurückzugeben und die App darüber zu informieren Login war erfolgreich. Es gibt auch einen _checkForErrors-Helfer, der alle Fehler behandelt. Schließlich gibt es eine perform-Funktion, die den Anmeldevorgang startet (und die einzige öffentliche Funktion in der Klasse ist). Schließlich erben wir von EventEmitter, um die Verwendung dieser Klasse zu vereinfachen.

Der Helfer

Die Funktion _checkForErrors prüft, ob ein Fehler aufgetreten ist oder ob die SQL-Abfrage keine Zeilen zurückgibt, und gibt den entsprechenden Fehler aus (mit dem angegebenen Grund):

Es wird auch true oder false zurückgegeben, je nachdem, ob ein Fehler aufgetreten ist oder nicht.

Login durchführen

Die Funktion perform muss nur eine Operation ausführen: Führen Sie die erste SQL-Abfrage aus (um zu überprüfen, ob der Benutzername vorhanden ist) und weisen Sie den entsprechenden Rückruf zu:

Ich gehe davon aus, dass Sie auf Ihre SQL-Verbindung in der sql-Variablen global zugreifen können (nur zur Vereinfachung würde die Diskussion darüber, ob dies eine gute Vorgehensweise ist, den Rahmen dieses Artikels sprengen). Und das wars für diese Funktion.

Überprüfen des Benutzernamens

Der nächste Schritt besteht darin, zu überprüfen, ob der Benutzername korrekt ist, und in diesem Fall die zweite Abfrage auszulösen, um das Kennwort zu überprüfen:

Ziemlich der gleiche Code wie im unordentlichen Beispiel, mit Ausnahme der Fehlerbehandlung.

Überprüfen des Passworts

Diese Funktion ist fast genau dieselbe wie die vorherige. Der einzige Unterschied besteht in der Abfrage:

Abrufen der nutzerbezogenen Daten

Die letzte Funktion in dieser Klasse ruft die Daten ab, die sich auf den Benutzer beziehen (der optionale Schritt), und löst damit ein Erfolgsereignis aus:

Letzte Berührungen und Verwendung

Als letztes müssen Sie die Klasse exportieren. Fügen Sie diese Zeile nach dem gesamten Code hinzu:

Dadurch wird die Login-Klasse zum einzigen Objekt, das das Modul exportiert. Es kann später folgendermaßen verwendet werden (vorausgesetzt, Sie haben die Moduldatei login.js benannt und sie befindet sich im selben Verzeichnis wie das Hauptskript):

Hier sind noch ein paar Codezeilen, aber die Lesbarkeit des Codes hat sich merklich verbessert. Außerdem verwendet diese Lösung keine externen Bibliotheken, was sie perfekt macht, wenn jemand Neues in Ihr Projekt kommt.

Das war der erste Ansatz, fahren wir mit dem zweiten fort.


Lösung 2: Versprechens-Objekt

Das Verwendung von Promise ist eine weitere Möglichkeit, dieses Problem zu lösen. Ein Versprechen (wie Sie im bereitgestellten Link lesen können) "repräsentiert den eventuellen Wert, der nach dem einmaligen Abschluss einer Operation zurückgegeben wird". In der Praxis bedeutet dies, dass Sie die Aufrufe verketten können, um die Pyramide zu glätten und den Code leichter lesbar zu machen.

Wir werden das Q-Modul verwenden, das im NPM-Repository verfügbar ist.

Q auf den Punkt gebracht

Bevor wir beginnen, möchte ich Ihnen das Q vorstellen. Für statische Klassen (Module) verwenden wir hauptsächlich die Funktion Q.nfcall. Es hilft uns bei der Konvertierung jeder Funktion, die den Node.js folgt Rückrufmuster (wobei die Parameter des Rückrufs der Fehler und das Ergebnis sind) zu einem Versprechen. Es wird so verwendet:

Es ist ziemlich ähnlich wie Object.prototype.call. Sie können auch Q.nfapply verwenden, das Object.prototype.apply ähnelt:

Wenn wir das Versprechen erstellen, fügen wir jeden Schritt mit der then(stepCallback)-Methode hinzu, fangen die Fehler mit catch(errorCallback) ab und beenden sie mit done().

In diesem Fall müssen wir Q.ninvoke oder Q.npost verwenden, da das sql-Objekt eine Instanz und keine statische Klasse ist. Der Unterschied besteht darin, dass wir den Methodennamen im ersten Argument als Zeichenfolge und die Instanz der Klasse, mit der wir arbeiten möchten, als zweite übergeben, um zu vermeiden, dass die Methode von der Instanz getrennt wird.

Die Vorbereitung des Promises

Das erste, was Sie tun müssen, ist, den ersten Schritt mit Q.nfcall oder Q.nfapply auszuführen (verwenden Sie den, den Sie mehr mögen, darunter gibt es keinen Unterschied):

Beachten Sie das Fehlen eines Semikolons am Ende der Zeile - die Funktionsaufrufe werden verkettet, sodass sie nicht vorhanden sein können. Wir rufen nur die Datei sql.query auf, wie im unordentlichen Beispiel, aber wir lassen den Rückrufparameter weg - er wird vom Versprechen behandelt.

Überprüfen des Benutzernamens

Jetzt können wir den Rückruf für die SQL-Abfrage erstellen. Er ist fast identisch mit dem im Beispiel "Pyramide of Doom". Fügen Sie dies nach dem Q.ninvoke-Aufruf hinzu:

Wie Sie sehen, hängen wir den Rückruf (den nächsten Schritt) mit der then-Methode an. Außerdem lassen wir im Rückruf den error parameter weg, da wir später alle Fehler abfangen werden. Wir prüfen manuell, ob die Abfrage etwas zurückgegeben hat, und wenn ja, geben wir das nächste auszuführende Versprechen zurück (wieder kein Semikolon wegen der Verkettung).

Überprüfen des Passworts

Wie beim Modularisierungsbeispiel ist das Überprüfen des Kennworts fast identisch mit dem Überprüfen des Benutzernamens. Dies sollte direkt nach then letzten Aufruf erfolgen:

Abrufen der nutzerbezogenen Daten

Der letzte Schritt ist der, in dem wir die Benutzerdaten in die Sitzung einfügen. Wiederum unterscheidet sich der Rückruf nicht wesentlich von dem chaotischen Beispiel:

Auf Fehler prüfen

Bei Verwendung von Versprechungen und der Q-Bibliothek werden alle Fehler vom Rückrufsatz mit der catch-Methode behandelt. Hier senden wir nur den HTTP 500, unabhängig davon, um welchen Fehler es sich handelt, wie in den obigen Beispielen:

Danach müssen wir die Methode done aufrufen, um sicherzustellen, dass ein Fehler, der nicht vor dem Ende behandelt wird, erneut ausgelöst und gemeldet wird (aus der README-Datei der Bibliothek). Jetzt sollte unser wunderschön abgeflachter Code so aussehen (und sich genauso verhalten wie der unordentliche):

Der Code ist viel sauberer und erfordert weniger Umschreiben als der Modularisierungsansatz.


Lösung 3: Schrittbibliothek

Diese Lösung ähnelt der vorherigen, ist jedoch einfacher. Q ist ein bisschen schwer, weil es die ganze Versprechen-Idee umsetzt. Die Step-Bibliothek dient nur dazu, die Callback-Hölle zu reduzieren. Es ist auch etwas einfacher zu verwenden, da Sie nur die einzige Funktion aufrufen, die aus dem Modul exportiert wird, alle Ihre Rückrufe als Parameter übergeben und this anstelle jedes Rückrufs verwenden. Das unordentliche Beispiel kann also mithilfe des Step-Moduls in dieses konvertiert werden:

Der Nachteil hierbei ist, dass es keinen gemeinsamen Fehlerbehandler gibt. Obwohl alle Ausnahmen, die in einem Rückruf ausgelöst werden, als erster Parameter an den nächsten übergeben werden (damit das Skript aufgrund der nicht erfassten Ausnahme nicht ausfällt), ist es meistens praktisch, einen Handler für alle Fehler zu haben.


Welches soll ich wählen?

Das ist so ziemlich eine persönliche Entscheidung, aber um Ihnen bei der Auswahl der richtigen zu helfen, finden Sie hier eine Liste der Vor- und Nachteile der einzelnen Ansätze:

Modularisierung:

Vorteile:

  • Keine externen Bibliotheken
  • Hilft, den Code wiederverwendbarer zu machen

Nachteile:

  • Mehr Code
  • Viel Umschreiben, wenn Sie ein vorhandenes Projekt konvertieren

Versprechen (Q):

Vorteile:

  • Weniger Code
  • Nur ein kleines Umschreiben, wenn es auf ein vorhandenes Projekt angewendet wird

Nachteile:

  • Sie müssen eine externe Bibliothek verwenden
  • Erfordert ein bisschen Lernen

Schrittbibliothek:

Vorteile:

  • Einfach zu bedienen, kein Lernen erforderlich
  • Ziemlich kopieren und einfügen, wenn ein vorhandenes Projekt konvertiert wird

Nachteile:

  • Kein allgemeiner Fehlerbehandler
  • Etwas schwieriger, diese step funktion richtig einzurücken

Abschluss

Wie Sie sehen können, kann die asynchrone Natur von Node.js verwaltet und die Rückrufhölle vermieden werden. Ich persönlich verwende den Modularisierungsansatz, weil ich meinen Code gerne gut strukturiert habe. Ich hoffe, diese Tipps helfen Ihnen dabei, Ihren Code besser lesbar zu schreiben und Ihre Skripte einfacher zu debuggen.

Advertisement
Advertisement
Advertisement
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.