Es wird viel über asynchrones Programmieren geredet, aber was genau ist die große Sache? Die große Sache ist, dass wir möchten, dass unser Code nicht blockiert.
Zu den Aufgaben, die unsere Anwendung blockieren können, gehören das Erstellen von HTTP-Anforderungen, das Abfragen einer Datenbank oder das Öffnen einer Datei. Einige Sprachen wie Java behandeln dies, indem sie mehrere Threads erstellen. JavaScript hat jedoch nur einen Thread, daher müssen wir unsere Programme so gestalten, dass keine Aufgabe den Fluss blockiert.
Die asynchrone Programmierung löst dieses Problem. Dadurch können wir Aufgaben später ausführen, damit nicht das gesamte Programm auf den Abschluss von Aufgaben wartet. Dies ist auch hilfreich, wenn Sie sicherstellen möchten, dass Aufgaben sequentiell ausgeführt werden.
In Teil eins dieses Tutorials lernen wir die Konzepte für synchronen und asynchronen Code kennen und erfahren, wie wir Rückruffunktionen verwenden können, um Probleme mit der Asynchronität zu lösen.
Ich möchte, dass Sie sich an das letzte Mal erinnern, als Sie im Lebensmittelgeschäft einkaufen waren. Es waren wahrscheinlich mehrere Registrierkassen für Kunden verfügbar. Dies hilft dem Geschäft, mehr Transaktionen in derselben Zeitspanne zu verarbeiten. Dies ist ein Beispiel für Parallelität.
Einfach gesagt: Parallelität erledigt mehrere Aufgaben gleichzeitig. Ihr Betriebssystem ist gleichzeitig, da mehrere Prozesse gleichzeitig ausgeführt werden. Ein Prozess ist eine Ausführungsumgebung oder eine Instanz einer laufenden Anwendung. Beispielsweise sind Ihr Browser, Ihr Texteditor und Ihre Antivirensoftware alle Prozesse auf Ihrem Computer, die gleichzeitig ausgeführt werden.
Anwendungen können auch gleichzeitig sein. Dies wird mit Threads erreicht. Ich werde nicht zu tief gehen, da dies den Rahmen dieses Artikels sprengt. Wenn Sie eine ausführliche Erläuterung der Funktionsweise von JavaScript unter der Haube wünschen, empfehle ich Ihnen, dieses Video anzuschauen.
Ein Thread ist eine Einheit innerhalb eines Prozesses, die Code ausführt. In unserem Store-Beispiel wäre jede Checkout-Zeile ein Thread. Wenn wir nur eine Kassenzeile im Geschäft haben, würde sich dies ändern, wie wir Kunden bearbeiten.
Waren Sie schon einmal in einer Schlange und hatten Sie etwas mit Ihrer Transaktion zu tun? Vielleicht brauchten Sie eine Preisüberprüfung oder einen Manager. Wenn ich in der Post bin und versuche, ein Paket zu versenden, und meine Etiketten nicht ausgefüllt sind, bittet mich der Kassierer, zur Seite zu treten, während sie andere Kunden auschecken. Wenn ich fertig bin, gehe ich zurück an die Spitze der Linie, um sie zu überprüfen.
Dies ist ähnlich wie bei der asynchronen Programmierung. Der Kassierer hätte auf mich warten können. Manchmal tun sie es. Aber es ist eine bessere Erfahrung, die Linie nicht zu halten und die anderen Kunden auszuchecken. Der Punkt ist, dass Kunden nicht in der Reihenfolge ihres Eincheckens ausgecheckt werden müssen. Ebenso muss Code nicht in der Reihenfolge ausgeführt werden, in der wir ihn schreiben.
Es ist naheliegend, dass unser Code sequentiell von oben nach unten ausgeführt wird. Das ist synchron. Mit JavaScript sind einige Aufgaben jedoch inhärent asynchron (z. B. setTimeout) und einige Aufgaben, die wir als asynchron entwerfen, da wir im Voraus wissen, dass sie blockieren können.
Schauen wir uns ein praktisches Beispiel mit Dateien in Node.js an. Wenn Sie die Codebeispiele ausprobieren möchten und einen Primer für die Verwendung von Node.js benötigen, finden Sie in diesem Lernprogramm Anweisungen zum Einstieg. In diesem Beispiel öffnen wir eine Datei mit Beiträgen und rufen einen der Beiträge ab. Dann öffnen wir eine Datei mit Kommentaren und rufen die Kommentare für diesen Beitrag ab.
Dies ist der synchrone Weg:
const fs = required ('fs'); const Pfad = Anfordern ('Pfad'); const postsUrl = pfad.join (__ dirname, 'db / posts.json'); const commentsUrl = pfad.join (__ dirname, 'db / comments.json'); // Rückgabe der Daten aus unserer Datei function loadCollection (url) try const response = fs.readFileSync (url, 'utf8'); return JSON.parse (Antwort); catch (Fehler) console.log (Fehler); // Rückgabe eines Objekts nach ID function getRecord (collection, id) return collection.find (function (element) return element.id == id;); // ein Array von Kommentaren für eine Post-Funktion zurückgeben getCommentsByPost (comments, postId) return comments.filter (function (comment) return comment.postId == postId;); // Initialisierungscode const posts = loadCollection (postsUrl); const post = getRecord (Beiträge, "001"); const comments = loadCollection (commentsUrl); const postComments = getCommentsByPost (Kommentare, post.id); console.log (Beitrag); console.log (postComments);
["id": "001", "title": "Begrüßung", "text": "Hello World", "author": "Jane Doe", "id": "002", "title": "JavaScript 101", "text": "Die Grundlagen der Programmierung.", "Author": "Alberta Williams", "id": "003", "title": "Async-Programmierung", "text": " Rückrufe, Versprechen und Async / Await. "," Autor ":" Alberta Williams "]
["id": "phx732", "postId": "003", "text": "Ich bekomme diesen Callback-Kram nicht." , "id": "avj9438", "postId": "003", "text": "Das ist wirklich nützliche Information." , "id": "gnk368", "postId": "001", "text": "Dies ist ein Testkommentar." ]
Das readFileSync
Methode öffnet die Datei synchron. Daher können wir unseren Initialisierungscode auch synchron schreiben. Dies ist jedoch nicht der beste Weg, um die Datei zu öffnen, da dies eine potenziell blockierende Aufgabe ist. Das Öffnen der Datei sollte asynchron erfolgen, damit der Ausführungsfluss kontinuierlich sein kann.
Knoten hat eine readFile
Methode, mit der wir die Datei asynchron öffnen können. Dies ist die Syntax:
fs.readFile (URL, 'utf8', Funktion (Fehler, Daten) …);
Wir könnten versucht sein, unsere Daten innerhalb dieser Callback-Funktion zurückzugeben, sie stehen uns jedoch nicht zur Verfügung loadCollection
Funktion. Unser Initialisierungscode muss sich ebenfalls ändern, da wir den Variablen nicht die richtigen Werte zuweisen können.
Um das Problem zu veranschaulichen, betrachten wir ein einfacheres Beispiel. Was denken Sie, wird der folgende Code gedruckt?
function task1 () setTimeout (function () console.log ('first');, 0); function task2 () console.log ('second'); function task3 () console.log ('dritte'); Aufgabe 1(); task2 (); task3 ();
In diesem Beispiel werden "second", "second" und dann "first" gedruckt. Es ist egal, dass das setTimeout
Funktion hat eine Verzögerung von 0. Es ist eine asynchrone Aufgabe in JavaScript, daher wird die spätere Ausführung immer zurückgestellt. Das firstTask
Funktion kann jede asynchrone Aufgabe darstellen, z. B. das Öffnen einer Datei oder das Abfragen unserer Datenbank.
Eine Lösung, um unsere Aufgaben in der gewünschten Reihenfolge auszuführen, ist die Verwendung von Rückruffunktionen.
Um Callback-Funktionen zu verwenden, übergeben Sie eine Funktion als Parameter an eine andere Funktion und rufen die Funktion dann auf, wenn Ihre Task abgeschlossen ist. Wenn Sie eine Einführung in die Verwendung von Funktionen höherer Ordnung benötigen, können Sie ein interaktives Lernprogramm von interactivex ausprobieren.
Rückrufe lassen uns die Ausführung von Aufgaben erzwingen. Sie helfen uns auch, wenn wir Aufgaben haben, die von den Ergebnissen einer vorherigen Aufgabe abhängen. Mit Hilfe von Rückrufen können wir unser letztes Beispiel korrigieren, damit es zuerst "," zweite "und dann" dritte "gedruckt wird..
Funktion zuerst (cb) setTimeout (function () return cb ('first');, 0); Funktion second (cb) return cb ('second'); Funktion Third (cb) return cb ('dritte'); first (function (result1) console.log (result1); second (funktion (result2)) console.log (result2); drittes (function (result3) console.log (result3););); );
Anstatt die Zeichenfolge in jeder Funktion zu drucken, geben wir den Wert in einem Rückruf zurück. Wenn unser Code ausgeführt wird, drucken wir den Wert, der in unseren Rückruf übergeben wurde. Das ist was unser readFile
Funktion verwendet.
Um auf unser Beispiel zuzugreifen, können wir unser ändern loadCollection
Funktion, so dass Callbacks verwendet werden, um die Datei asynchron zu lesen.
Funktion loadCollection (URL, Callback) fs.readFile (URL, 'utf8', Funktion (Fehler, Daten) if (Fehler) console.log (error); else return callback (JSON.parse (data)) ;);
Und so sieht unser Initialisierungscode mit Callbacks aus:
loadCollection (postsUrl, Funktion (posts) loadCollection (commentsUrl, Funktion (Kommentare) getRecord (posts, "001", Funktion (post) const postComments = getCommentsByPost (comments, post.id); console.log (post); console.log (postComments););););
Eine Sache zu beachten in unserem loadCollection
Funktion ist das, anstatt ein zu verwenden versuchen / fangen
Anweisung zur Fehlerbehandlung verwenden wir eine ansonsten
Aussage. Der catch-Block kann keine Fehler abfangen, die vom zurückgegeben werden readFile
Ruf zurück.
Es ist empfehlenswert, Fehlerbehandlungsroutinen für Fehler zu verwenden, die auf äußere Einflüsse im Gegensatz zu Programmierfehlern zurückzuführen sind. Dies umfasst den Zugriff auf Dateien, das Herstellen einer Verbindung zu einer Datenbank oder das Durchführen einer HTTP-Anforderung.
In dem überarbeiteten Codebeispiel habe ich keine Fehlerbehandlung enthalten. Wenn bei einem der Schritte ein Fehler auftritt, wird das Programm nicht fortgesetzt. Es wäre schön, sinnvolle Anweisungen zu geben.
Ein Beispiel für die Fehlerbehandlung ist, wenn wir einen Benutzer anmelden müssen. Dazu müssen Sie einen Benutzernamen und ein Kennwort aus einem Formular abrufen, unsere Datenbank abfragen, um festzustellen, ob es sich um eine gültige Kombination handelt, und den Benutzer anschließend zu seinem Dashboard umleiten, wenn wir erfolgreich sind. Wenn der Benutzername und das Passwort ungültig sind, wird unsere App nicht mehr ausgeführt, wenn wir nicht sagen, was zu tun ist.
Eine bessere Benutzererfahrung wäre, eine Fehlermeldung an den Benutzer zurückzusenden und ihnen zu erlauben, sich erneut anzumelden. In unserem Dateibeispiel könnten wir ein Fehlerobjekt in der Rückruffunktion mitgeben. Dies ist der Fall bei der readFile
Funktion. Wenn wir dann den Code ausführen, können wir eine ansonsten
Anweisung, um das erfolgreiche Ergebnis und das abgelehnte Ergebnis zu behandeln.
Schreiben Sie mit der Rückrufmethode ein Programm, das eine Benutzerdatei öffnet, wählen Sie einen Benutzer aus, öffnen Sie eine Datei mit Beiträgen und drucken Sie die Benutzerinformationen und alle ihre Beiträge.
Die asynchrone Programmierung ist eine Methode, die in unserem Code verwendet wird, um Ereignisse für die spätere Ausführung zu verschieben. Wenn Sie sich mit einer asynchronen Aufgabe befassen, stellen Rückrufe eine Lösung dar, um die zeitliche Abfolge unserer Aufgaben so festzulegen, dass sie sequentiell ausgeführt werden.
Wenn mehrere Aufgaben vorhanden sind, die vom Ergebnis der vorherigen Aufgaben abhängen, besteht eine Lösung darin, mehrere geschachtelte Rückrufe zu verwenden. Dies könnte jedoch zu einem Problem führen, das als "Callback-Hölle" bezeichnet wird. Versprechungen lösen das Problem mit der Callback-Hölle, und async-Funktionen lassen uns unseren Code synchron schreiben. In Teil 2 dieses Tutorials erfahren Sie, was sie sind und wie Sie sie in unserem Code verwenden können.