Rückrufprobleme mit Async lösen

Beim ersten Start der Programmierung erfahren wir, dass ein Codeblock von oben nach unten ausgeführt wird. Dies ist eine synchrone Programmierung: Jede Operation ist abgeschlossen, bevor die nächste beginnt. Dies ist ideal, wenn Sie viele Dinge erledigen, für die ein Computer praktisch keine Zeit benötigt, z. B. das Hinzufügen von Zahlen, das Bearbeiten einer Zeichenfolge oder das Zuweisen von Variablen. 

Was passiert, wenn Sie etwas tun möchten, das relativ lange dauert, z. B. auf eine Datei auf der Festplatte zugreifen, eine Netzwerkanforderung senden oder auf den Ablauf eines Timers warten? Bei der synchronen Programmierung kann Ihr Skript während des Wartens nichts anderes tun. 

Dies kann für einfache Dinge geeignet sein oder in Situationen, in denen Sie mehrere Instanzen Ihres Skripts ausführen. Für viele Serveranwendungen ist dies jedoch ein Albtraum. 

Geben Sie die asynchrone Programmierung ein. In einem asynchronen Skript wird der Code weiterhin ausgeführt, während auf etwas gewartet wird. Er kann jedoch zurückspringen, wenn etwas passiert ist. 

Nehmen Sie zum Beispiel eine Netzwerkanfrage. Wenn Sie eine Netzwerkanforderung an einen langsamen Server senden, für die eine Antwort drei volle Sekunden dauert, kann Ihr Skript aktiv andere Aufgaben ausführen, während dieser langsame Server antwortet. In diesem Fall scheinen drei Sekunden für einen Menschen nicht viel zu sein, aber ein Server könnte während des Wartens auf Tausende anderer Anforderungen reagieren. Wie gehen Sie also mit der Asynchronität in Node.js um?? 

Der grundlegendste Weg ist durch einen Rückruf. Ein Rückruf ist nur eine Funktion, die aufgerufen wird, wenn ein asynchroner Vorgang abgeschlossen ist. Die Callback-Funktionen von Node.js verfügen standardmäßig über mindestens ein Argument, Irr. Rückrufe können mehr Argumente enthalten (die normalerweise die an den Rückruf zurückgegebenen Daten darstellen), der erste wird jedoch sein Irr. Wie du vielleicht erraten hast, Irr enthält ein Fehlerobjekt (falls später ein Fehler ausgelöst wurde).

Schauen wir uns ein sehr einfaches Beispiel an. Wir werden das eingebaute Dateisystemmodul von Node.js verwenden (fs). In diesem Skript lesen wir den Inhalt einer Textdatei. Die letzte Zeile der Datei ist a console.log Das wirft eine Frage auf: Wenn Sie dieses Skript ausführen, sehen Sie das Protokoll, bevor wir den Inhalt der Textdatei sehen?

var fs = required ('fs'); fs.readFile ('a-text-file.txt', // der Dateiname einer Textdatei mit der Aufschrift 'Hello!' 'utf8'), // die Kodierung der Datei, in diesem Fall die Funktion utf-8 (err , text) // der Rückruf console.log ('Error:', err); // Fehler, falls konsolen.log ('Text:', text); // der Inhalt der Datei); // Wird dies vor oder nach dem Fehler / Text sein? console.log ('Wird dies vor oder nach dem Inhalt der Textdatei protokolliert?'); 

Da dies asynchron ist, sehen wir tatsächlich das letzte console.log Vor der Inhalt der Textdatei. Wenn Sie eine Datei haben a-text-file.txt In demselben Verzeichnis, in dem Sie Ihr Knotenskript ausführen, wird dies angezeigt Irr ist Null, und der Wert von Text wird mit dem Inhalt der Textdatei gefüllt. 

Wenn Sie keinen Dateinamen haben a-text-file.txt, Irr gibt ein Fehlerobjekt und den Wert von zurück Text wird sein nicht definiert. Dies führt zu einem wichtigen Aspekt von Rückrufen: Sie müssen immer mit Ihren Fehlern umgehen. Um Fehler behandeln zu können, müssen Sie einen Wert in der IrrVariable; Wenn ein Wert vorhanden ist, ist ein Fehler aufgetreten. Vereinbarungs, Irr Argumente kehren normalerweise nicht zurück falsch, so kannst du nur auf wahrheit prüfen.

var fs = required ('fs'); fs.readFile ('a-text-file.txt', // der Dateiname einer Textdatei mit der Aufschrift 'Hello!' 'utf8'), // die Kodierung der Datei, in diesem Fall die Funktion utf-8 (err , text) // der Rückruf if (err) console.error (err); // zeigt einen Fehler an der Konsole an else console.log ('Text:', text); // kein Fehler, also anzeigen der Inhalt der Datei);

Angenommen, Sie möchten den Inhalt von zwei Dateien in einer bestimmten Reihenfolge anzeigen. Sie werden am Ende mit so etwas enden:

var fs = required ('fs'); fs.readFile ('a-text-file.txt', // der Dateiname einer Textdatei mit der Aufschrift 'Hello!' 'utf8'), // die Kodierung der Datei, in diesem Fall die Funktion utf-8 (err , text) // der Rückruf if (err) console.error (err); // zeigt einen Fehler an der Konsole an else console.log ('Erste Textdatei:', Text); // kein Fehler, Zeigen Sie also den Inhalt der Datei fs.readFile an ('another-text-file.txt', // den Dateinamen einer Textdatei mit der Aufschrift 'Hello!' 'utf8'), // in diesem Fall die Kodierung der Datei , utf-8-Funktion (err, text) // der Rückruf if (err) console.error (err); // zeigt einen Fehler an der Konsole an else console.log ('Zweite Textdatei:', Text // Kein Fehler, also den Inhalt der Datei anzeigen););

Der Code sieht ziemlich unangenehm aus und hat eine Reihe von Problemen:

  1. Sie laden die Dateien nacheinander. Es wäre effizienter, wenn Sie beide gleichzeitig laden und die Werte zurückgeben könnten, wenn beide vollständig geladen wurden.

  2. Syntaktisch ist es korrekt, aber schwer lesbar. Beachten Sie die Anzahl der verschachtelten Funktionen und die zunehmenden Registerkarten. Sie könnten ein paar Tricks machen, um es etwas besser aussehen zu lassen, aber Sie könnten die Lesbarkeit auf andere Weise opfern.

  3. Es ist nicht sehr allgemeiner Zweck. Dies funktioniert gut für zwei Dateien, aber was wäre, wenn Sie neun Dateien und manchmal 22 oder nur eine hatten? Die Art und Weise, wie es derzeit geschrieben wird, ist sehr starr.

Keine Sorge, wir können all diese Probleme (und mehr) mit async.js lösen.

Rückrufe mit Async.js

Beginnen wir mit der Installation des Moduls async.js.

npm install async --save

Async.js kann verwendet werden, um Arrays von Funktionen in Reihe oder parallel zusammenzukleben. Lassen Sie uns unser Beispiel neu schreiben:

var async = required ('async'), //async.js-Modul fs = required ('fs'); async.series (// führe die Funktionen im ersten Argument nacheinander aus [// Das erste Argument ist ein Array von Funktionen function (cb) // 'cb' ist die Abkürzung für "callback") fs.readFile ('a- text-file.txt ',' utf8 ', cb);, Funktion (cb) fs.readFile (' another-text-file.txt ',' utf8 ', cb);], Funktion (err, values ) // Der "erledigte" Callback, der ausgeführt wird, nachdem die Funktionen im Array abgeschlossen sind. If (err) // Wenn beim Ausführen von Funktionen im Array Fehler aufgetreten sind, werden sie als err gesendet. Console.error ( err); else // Wenn err falsch ist, dann ist alles gut. console.log ('Erste Textdatei:', Werte [0]); Console.log ('Zweite Textdatei:', Werte [1]); );

Dies funktioniert fast genauso wie im vorherigen Beispiel, indem jede Datei sequentiell geladen wird. Sie unterscheidet sich nur dadurch, dass sie jede Datei liest und das Ergebnis nicht anzeigt, bis sie vollständig ist. Der Code ist prägnanter und sauberer als im vorherigen Beispiel (und wir werden es später noch besser machen). async.series nimmt ein Array von Funktionen und führt sie nacheinander aus. 

Jede Funktion sollte nur ein einziges Argument haben, den Rückruf (oder cb in unserem Code). cbsollte mit der gleichen Art von Argumenten wie jeder andere Rückruf ausgeführt werden, damit wir ihn direkt in unseren einfügen können fs.readFile Argumente. 

Schließlich werden die Ergebnisse an den letzten Rückruf gesendet, das zweite Argument in async.series. Die Ergebnisse werden in einem Array gespeichert, dessen Werte mit der Reihenfolge der Funktionen im ersten Argument von übereinstimmen async.series.

Bei async.js wird die Fehlerbehandlung vereinfacht, da bei einem Fehler der Fehler an das Argument des finalen Callbacks zurückgegeben wird und keine weiteren asynchronen Funktionen ausgeführt werden. 

Jetzt alle zusammen

Eine verwandte Funktion ist async.parallel; es hat die gleichen Argumente wie async.series Sie können also zwischen den beiden wechseln, ohne den Rest Ihrer Syntax zu ändern. Dies ist ein guter Punkt, um Parallele und Parallele zu behandeln. 

JavaScript ist im Grunde eine Singlethread-Sprache, was bedeutet, dass jeweils nur eine Sache ausgeführt werden kann. Es ist in der Lage, einige Aufgaben in einem separaten Thread auszuführen (zum Beispiel die meisten E / A-Funktionen). Hier kommt die asynchrone Programmierung mit JS ins Spiel. Verwechseln Sie nicht parallel mit Parallelität

Wenn Sie zwei Dinge mit ausführen async.parallel, Sie machen es nicht, einen anderen Thread zu öffnen, um JavaScript zu parsen oder zwei Dinge gleichzeitig zu tun - Sie steuern wirklich, wenn sie im ersten Argument von zwischen Funktionen übergeben werden async.parallel. Sie gewinnen also nichts, indem Sie synchronen Code in async.parallel einfügen. 

Dies lässt sich am besten visuell erklären:

Hier ist unser vorheriges Beispiel so geschrieben, dass es parallel ist - der einzige Unterschied ist, dass wir es verwenden async.parallel eher, als async.series.

var async = required ('async'), //async.js-Modul fs = required ('fs'); async.parallel (// führe die Funktionen im ersten Argument aus, aber warte nicht, bis die erste Funktion beendet ist, um die zweite zu starten [// Das erste Argument ist ein Array mit Funktionen function (cb) // 'cb' ist Abkürzung für "callback" fs.readFile ('a-text-file.txt', 'utf8', cb);, Funktion (cb) fs.readFile ('another-text-file.txt', 'utf8 ', cb);], function (err, values) // Der "done" Callback, der ausgeführt wird, nachdem die Funktionen im Array abgeschlossen sind. if (err) // Wenn bei der Ausführung von Funktionen im Array Fehler aufgetreten sind , werden sie als err.konsole.error (err) gesendet; else // Wenn err falsch ist, dann ist alles gut console.log ('Erste Textdatei:', Werte [0]); console.log ( 'Zweite Textdatei:', Werte [1]););

Über und über

In den vorherigen Beispielen wurde eine bestimmte Anzahl von Operationen ausgeführt. Was passiert jedoch, wenn Sie eine variable Anzahl asynchroner Operationen benötigen? Dies wird schnell unübersichtlich, wenn Sie sich nur auf Rückrufe und das reguläre Sprachkonstrukt verlassen, auf unbeholfene Leistungsindikatoren oder Bedingungsprüfungen, die die eigentliche Bedeutung Ihres Codes verdecken. Schauen wir uns das grobe Äquivalent einer for-Schleife mit async.js an.

In diesem Beispiel schreiben wir zehn Dateien mit sequenziellen Dateinamen und einigen kurzen Inhalten in das aktuelle Verzeichnis. Sie können die Anzahl variieren, indem Sie den Wert des ersten Arguments von ändern async.times. In diesem Beispiel ist der Rückruf für fs.writeFile schafft nur ein Irr Argument, aber die async.times Funktion kann auch einen Rückgabewert unterstützen. Es wird wie async.series als zweiter Array an den done-Callback im zweiten Argument übergeben.

var async = required ('async'), fs = required ('fs'); async.times (10, // Anzahl der Ausführungszeiten der Funktion function (runCount, Callback) fs.writeFile ('file -' + runCount + '. txt') // der neue Dateiname 'Dies ist die Dateinummer' runCount, // der Inhalt der neuen Datei (callback)); function (err) if (err) console.error (err); else console.log ('Wrote files.'););

Es ist eine gute Zeit zu sagen, dass die meisten async.js-Funktionen standardmäßig parallel statt seriell ausgeführt werden. Im obigen Beispiel werden die Dateien und der Bericht erstellt, wenn alle Dateien vollständig erstellt und geschrieben wurden. 

Die Funktionen, die standardmäßig parallel ausgeführt werden, verfügen über eine Folgeserienfunktion, die durch die Funktion mit der Endung "Serie" gekennzeichnet wird. Wenn Sie dieses Beispiel in Serie anstatt parallel ausführen möchten, würden Sie dies ändern async.times zu async.timesSeries.

Für unser nächstes Schleifenbeispiel werfen wir einen Blick auf die Funktion async.until. async.bis führt eine asynchrone Funktion (in Reihe) aus, bis eine bestimmte Bedingung erfüllt ist. Diese Funktion akzeptiert drei Funktionen als Argumente. 

Die erste Funktion ist der Test, bei dem Sie entweder true zurückgeben (wenn Sie die Schleife stoppen möchten) oder false (wenn Sie die Schleife fortsetzen möchten). Das zweite Argument ist die asynchrone Funktion und das letzte Argument ist der erledigte Callback. Schauen Sie sich dieses Beispiel an:

var async = required ('async'), fs = required ('fs'), startTime = new Date (). getTime (), // der Unix-Zeitstempel in Millisekunden runCount = 0; async.until (function () // gibt true zurück, wenn 4 Millisekunden verstrichen sind, andernfalls false (und führt das Skript weiter aus) gibt new Date () zurück. getTime ()> (startTime + 5);, function (callback)  runCount + = 1; fs.writeFile ('timed-file -' + runCount + '. txt', // der neue Dateiname 'Dies ist die Dateinummer' + runCount, // der Inhalt des neuen Datei-Callbacks);, function (err) if (err) console.error (err); else console.log ('Wrote files.'););

Dieses Skript erstellt für fünf Millisekunden neue Textdateien. Beim Start des Skripts erhalten wir die Startzeit in der Millisekunden-Unix-Epoche und dann in der Testfunktion die aktuelle Zeit und testen, ob sie fünf Millisekunden größer ist als die Startzeit plus fünf. Wenn Sie dieses Skript mehrmals ausführen, erhalten Sie möglicherweise andere Ergebnisse. 

Auf meinem Computer erstellte ich in fünf Millisekunden zwischen 6 und 20 Dateien. Interessanterweise, wenn Sie versuchen, hinzuzufügen console.log In der Testfunktion oder in der asynchronen Funktion erhalten Sie sehr unterschiedliche Ergebnisse, da das Schreiben auf Ihre Konsole einige Zeit in Anspruch nimmt. Es zeigt sich nur, dass in der Software alles Kosten verursacht!

Die for-Schleife ist eine praktische Struktur, mit der Sie für jedes Element eines Arrays etwas tun können. In async.js wäre dies das async.each Funktion. Diese Funktion akzeptiert drei Argumente: die Auflistung oder das Array, die für jedes Element auszuführende asynchrone Funktion und den durchgeführten Rückruf. 

Im folgenden Beispiel nehmen wir ein Array von Strings (in diesem Fall Arten von Windhundrassen) und erstellen eine Datei für jeden String. Wenn alle Dateien erstellt wurden, wird der Callback ausgeführt. Wie zu erwarten, werden Fehler über die Irr Objekt im erledigten Callback. async.each wird parallel ausgeführt, aber wenn Sie es in Reihe ausführen möchten, können Sie dem zuvor erwähnten Muster folgen und es verwenden async.eachSeries anstatt async.each.

var async = required ('async'), fs = required ('fs'); async.each (// ein Array von Windhundrassen ['Windhund', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italian-greyhound'], Funktion ( dogBreed, callback) fs.writeFile (dogBreed + '. txt', // der neue Dateiname 'Datei für Hunde der Rasse' + dogBreed, // der Inhalt der neuen Datei callback);, Funktion (err)  if (err) console.error (err); else console.log ('Fertig mit dem Schreiben von Dateien über Hunde.');;

Ein Cousin von async.each ist der async.map Funktion; Der Unterschied ist, dass Sie die Werte an Ihren Callback zurückgeben können. Mit dem async.map Wenn Sie diese Funktion ausführen, übergeben Sie ein Array oder eine Collection als erstes Argument, und dann wird für jedes Element im Array oder die Collection eine asynchrone Funktion ausgeführt. Das letzte Argument ist der erledigte Rückruf. 

Das folgende Beispiel nimmt das Array von Hunderassen und verwendet jedes Element, um einen Dateinamen zu erstellen. Der Dateiname wird dann an übergeben fs.readFile, Dort wird es gelesen und die Werte werden von der Callback-Funktion zurückgegeben. Sie erhalten am Ende ein Array des Dateiinhalts in den Argumenten für durchgeführte Rückrufe.

var async = required ('async'), fs = required ('fs'); async.map (["windhund", "saluki", "borzoi", "galga", "podenco", "whippet", "lurcher", "italian-greyhound"]), funktion (dogBreed, callback) fs.readFile (dogBreed + '. txt', // der neue Dateiname 'utf8', callback);, Funktion (err, dogBreedFileContents) if (err) console.error (err); else console.log ('dog breeds '); console.log (dogBreedFileContents););

async.filter ist auch in der Syntax sehr ähnlich async.each und async.map, Mit dem Filter senden Sie jedoch einen booleschen Wert an den Elementrückruf und nicht an den Wert der Datei. Im erledigten Callback erhalten Sie ein neues Array mit nur den Elementen, die Sie übergeben haben wahr oder wahrer Wert für den Artikelrückruf. 

var async = required ('async'), fs = required ('fs'); async.filter (["windhund", "saluki", "borzoi", "galga", "podenco", "whippet", "lurcher", "italian-greyhound"]), Funktion (dogBreed, callback) fs.readFile (dogBreed + '. txt', // der neue Dateiname 'utf8', function (err, fileContents) if (err) callback (err); else callback (err, // dies wird falsch sein, da wir geprüft haben it über fileContents.match (/ greyhound / gi) // Verwenden Sie RegExp, um die Zeichenfolge 'greyhound' im Inhalt der Datei zu suchen.);, function (err, dogBreedFileContents) if (err) console .error (err); else console.log ('greyhound breeds:'); console.log (dogBreedFileContents););

In diesem Beispiel machen wir ein paar mehr Dinge als in den vorherigen Beispielen. Beachten Sie, wie wir einen zusätzlichen Funktionsaufruf hinzufügen und unseren eigenen Fehler behandeln. Das ob Irr und Rückruf (Fehler) pattern ist sehr nützlich, wenn Sie die Ergebnisse einer asynchronen Funktion bearbeiten müssen, aber dennoch möchten, dass async.js die Fehler verarbeitet. 

Außerdem werden Sie feststellen, dass wir die Variable err als erstes Argument für die Rückruffunktion verwenden. Auf den ersten Blick sieht das nicht ganz richtig aus. Aber da wir bereits auf die Wahrhaftigkeit des Irrtums geprüft haben, wissen wir, dass es falsch ist und sicher zum Callback weitergeleitet werden kann. 

Über den Rand einer Klippe

Bisher haben wir eine Reihe nützlicher Bausteine ​​untersucht, die in der synchronen Programmierung grobe Folgerungen haben. Lass uns direkt eintauchen async.waterfall, was in der synchronen Welt nicht viel von einem Äquivalent hat. 

Das Konzept eines Wasserfalls besteht darin, dass die Ergebnisse einer asynchronen Funktion in die Argumente einer anderen asynchronen Funktion in Reihe fließen. Es ist ein sehr leistungsfähiges Konzept, insbesondere wenn versucht wird, mehrere asynchrone Funktionen, die aufeinander angewiesen sind, aneinandergereiht. Mit async.waterfall, Das erste Argument ist ein Array von Funktionen und das zweite Argument ist Ihr Callback. 

In Ihrem Funktionsbereich beginnt die erste Funktion immer mit einem einzigen Argument, dem Callback. Jede nachfolgende Funktion sollte mit den Nicht-Err-Argumenten der vorherigen Funktion und nicht mit der Err-Funktion übereinstimmen und den neuen Callback hinzufügen.

In unserem nächsten Beispiel fangen wir an, einige Konzepte zu kombinieren, die den Wasserfall als verwenden kleben. In dem Array, das das erste Argument ist, haben wir drei Funktionen: Die erste lädt die Verzeichnisliste aus dem aktuellen Verzeichnis, die zweite übernimmt die Verzeichnisliste und verwendet sie async.map zu rennen fs.stat für jede Datei, und die dritte Funktion entnimmt die Verzeichnisliste aus dem ersten Funktionsergebnis und ruft den Inhalt für jede Datei ab (fs.readFile).

async.waterfall Führt jede Funktion nacheinander aus, so dass immer alle ausgeführt werden fs.stat Funktionen vor der Ausführung fs.readFile. In diesem ersten Beispiel sind die zweite und die dritte Funktion nicht voneinander abhängig, sodass sie in eine eingebunden werden können async.parallel Um die Gesamtausführungszeit zu reduzieren, ändern wir diese Struktur für das nächste Beispiel noch einmal.

Hinweis: Führen Sie dieses Beispiel in einem kleinen Verzeichnis mit Textdateien aus. Andernfalls werden Sie lange Zeit Müll in Ihrem Terminalfenster erhalten.

var async = required ('async'), fs = required ('fs'); async.waterfall ([function (callback) fs.readdir ('.', callback); // liest das aktuelle Verzeichnis und leitet es an die nächste Funktion weiter., function (fileNames, callback) // 'fileNames' ist die Verzeichnisliste aus der vorherigen Funktion async.map (fileNames, // Die Verzeichnisliste ist nur ein Array von Dateinamen, fs.stat, //). Daher können Sie async.map verwenden, um fs.stat für jede Dateinamenfunktion auszuführen (err , stats) if (err) callback (err); else callback (err, fileNames, stats); // den Fehler, die Verzeichnisliste und die stat-Auflistung an den nächsten Eintrag im Wasserfall weitergeben) ;, function (fileNames, stats, callback) // Die Verzeichnisliste, 'fileNames' wird durch die Auflistung von fs.stat-Objekten in 'stats' in async.map (fileNames, function (aFileName, readCallback) // Diesmal nehmen wir die Dateinamen mit map und übergeben sie an fs.readFile, um den Inhalt zu erhalten. Fs.readFile (aFileName, 'utf8', readCallback);, function (err, contents) if (err) callback (err); else // Nun unser Rückruf w Ich habe drei Argumente, die ursprüngliche Verzeichnisliste ('fileNames'), die fs.stats-Auflistung und ein Array mit dem Inhalt jedes Datei-Callbacks (err, fileNames, stats, Inhalt). ); ], function (err, fileNames, stats, Inhalt) if (err) console.error (err);  else console.log (Dateiname); console.log (Statistiken); console.log (Inhalt); );

Nehmen wir an, wir wollen nur die Ergebnisse von Dateien abrufen, deren Größe über 500 Byte liegt. Wir könnten den obigen Code verwenden, aber Sie erhalten die Größe und den Inhalt jeder Datei, unabhängig davon, ob Sie sie benötigen oder nicht. Wie könnten Sie nur den Status der Dateien und nur den Inhalt der Dateien erhalten, die die Größenanforderungen erfüllen? 

Zunächst können wir alle anonymen Funktionen in benannte Funktionen ziehen. Es ist eine persönliche Präferenz, aber es macht den Code ein wenig sauberer und verständlicher (wiederverwendbar zum Starten). Wie Sie sich vorstellen können, müssen Sie die Größen ermitteln, diese Größen auswerten und nur den Inhalt der Dateien über die Größenanforderung hinaus erhalten. Dies kann leicht mit so etwas erreicht werden Array.filter, Dies ist jedoch eine synchrone Funktion, und async.waterfall erwartet Funktionen im asynchronen Stil. Async.js hat eine Hilfsfunktion, die synchrone Funktionen in asynchrone Funktionen umwandeln kann, die eher jazzly genannt werden async.asyncify.

Wir müssen drei Dinge tun, mit denen wir alle umwickeln async.asyncify. Zuerst nehmen wir die Dateinamen- und Statistik-Arrays aus dem arrayFsStat Funktion, und wir werden sie mit zusammenführen Karte. Dann filtern wir alle Elemente mit einer Statistikgröße von weniger als 300. Zum Schluss nehmen wir den kombinierten Dateinamen und das stat-Objekt und verwenden es Karte noch einmal, um den Dateinamen herauszuholen. 

Nachdem wir die Namen der Dateien mit einer Größe von weniger als 300 erhalten haben, werden wir sie verwenden async.map und fs.readFile um den Inhalt zu erhalten. Es gibt viele Möglichkeiten, dieses Ei zu knacken, aber in unserem Fall wurde es gebrochen, um maximale Flexibilität und Code-Wiederverwendung zu zeigen. Diese async.waterfall Verwendung veranschaulicht, wie Sie synchronen und asynchronen Code mischen und anpassen können.

var async = required ('async'), fs = required ('fs'); // Unsere anonyme Funktion wurde in benannte Funktionen umgeändert. Function directoryListing (callback) fs.readdir ('.', Callback);  function arrayFsStat (fileNames, callback) async.map (fileNames, fs.stat, function (err, stats) if (err) callback (err); else callback (err, fileNames, stats); );  function arrayFsReadFile (Dateiname, Rückruf) async.map (Dateiname, Funktion (aFileName, readCallback) fs.readFile (aDateiname, 'utf8', readCallback);, Funktion (Fehler, Inhalt) if (err) callback (err); else callback (err, Inhalt););  // Diese Funktionen sind eine synchrone Funktion mergeFilenameAndStat (fileNames, stats) return stats.map (function (aStatObj, index) aStatObj.fileName = fileNames [index]; return aStatObj;);  function above300 (mixedFilenamesAndStats) return combinedFilenamesAndStats .filter (function (aStatObj) return aStatObj.size> = 300;);  function justFilenames (kombinierteDateinamenAndStats) return combinedFilenamesAndStats .map (function (aCombinedFileNameAndStatObj) return aCombinedFileNameAndStatObj.fileName;);  async.waterfall ([verzeichnisliste, arrayFsStat, async.asyncify (mergeFilenameAndStat), // asyncify umschließt synchrone Funktionen in einem err-first-Rückruf async.asyncify (über300), async.asyncify (justFilenames), arrayFsReadFile], Funktion (err, Inhalt) if (err) console.error (err); else console.log (Inhalt););

Wenn wir diesen Schritt noch weiter gehen, wollen wir unsere Funktion noch weiter verfeinern. Nehmen wir an, wir möchten eine Funktion schreiben, die genau wie oben funktioniert, aber mit der Flexibilität, in jeden Pfad zu schauen. Ein enger Verwandter von async.waterfall ist async.seq. Während async.waterfall führt einfach einen Wasserfall von Funktionen aus, async.seq gibt eine Funktion zurück, die einen Wasserfall mit anderen Funktionen ausführt. Sie können nicht nur eine Funktion erstellen, sondern auch Werte übergeben, die in die erste asynchrone Funktion übernommen werden. 

Umwandlung in async.seq nimmt nur wenige Änderungen vor. Zuerst ändern wir Verzeichnisliste ein Argument akzeptieren - dies wird der Pfad sein. Zweitens fügen wir eine Variable für unsere neue Funktion hinzu (directoryAbove300). Drittens nehmen wir das Array-Argument aus der async.waterfall und übersetzen das in Argumente für async.seq. Unser Callback für den Wasserfall wird jetzt als Callback beim Ausführen verwendet directoryAbove300.

var async = required ('async'), fs = requir ('fs'), verzeichnisAbove300; function directoryListing (initialPath, callback) // wir können eine Variable an die erste in async.seq verwendete Funktion übergeben - die resultierende Funktion kann Argumente akzeptieren und diese erste Funktion übergeben fs.readdir (initialPath, callback);  function arrayFsStat (fileNames, callback) async.map (fileNames, fs.stat, function (err, stats) if (err) callback (err); else callback (err, fileNames, stats); );  function arrayFsReadFile (Dateiname, Rückruf) async.map (Dateiname, Funktion (aFileName, readCallback) fs.readFile (aDateiname, 'utf8', readCallback);, Funktion (Fehler, Inhalt) if (err) callback (err); else callback (err, Inhalt););  function mergeFilenameAndStat (fileNames, stats) return stats.map (function (aStatObj, index) aStatObj.fileName = fileNames [index]; return aStatObj;);  function above300 (mixedFilenamesAndStats) return combinedFilenamesAndStats .filter (function (aStatObj) return aStatObj.size> = 300;);  function justFilenames (kombinierteDateinameAndStats) return CombinedFilenamesAndStats .map (Funktion (aCombinedFileNameAndStatObj) return aCombinedFileNameAndStatObj.fileName;) //async.seq erzeugt eine neue Funktion, die Sie über und über directoryAbove300 = verwenden können. arrayFsStat, async.asyncify (mergeFilenameAndStat), async.asyncify (über300), async.asyncify (justFilenames), arrayFsReadFile); directoryAbove300 ('.', Funktion (err, fileNames, stats, Inhalt) if (err) console.error (err); else console.log (fileNames);;

Ein Hinweis zu Versprechen und Asynchronfunktionen

Sie fragen sich vielleicht, warum ich Versprechen nicht erwähnt habe. Ich habe nichts gegen sie - sie sind ziemlich praktisch und vielleicht eine elegantere Lösung als Rückrufe -, aber sie sehen auf asynchrone Codierung anders. 

Integrierte Node.js-Module verwenden Irr-erste Rückrufe und Tausende von anderen Modulen verwenden dieses Muster. Aus diesem Grund verwendet dieses Tutorial fs In den Beispielen verwendet etwas, das so grundlegend ist wie der Zugriff auf das Dateisystem in Node.js, Callbacks. Daher ist das Versprechen von Callback-Codes ohne Versprechen ein wesentlicher Bestandteil der Node.js-Programmierung.  

Es ist möglich, so etwas wie Bluebird zu verwenden, um err-first-Callbacks in Promise-basierte Funktionen einzubinden, aber das bringt Sie nur so weit. Async.js bietet eine Vielzahl von Metaphern, die asynchronen Code lesbar und handhabbar machen.

Asynchronie umarmen

JavaScript ist zu einer der De-facto-Sprachen geworden, die im Web arbeiten. Es ist nicht ohne Lernkurven, und es gibt viele Frameworks und Bibliotheken, um Sie zu beschäftigen. Wenn Sie nach zusätzlichen Ressourcen suchen, um zu studieren oder in Ihrer Arbeit zu verwenden, schauen Sie nach, was wir auf dem Envato-Marktplatz zur Verfügung haben.

Aber asynchrones Lernen ist etwas völlig anderes und hoffentlich hat Ihnen dieses Tutorial gezeigt, wie nützlich es sein kann.

Asynchronität ist der Schlüssel zum Schreiben serverseitiger JavaScript-Codes. Wenn sie jedoch nicht ordnungsgemäß erstellt werden, kann Ihr Code zu einer unüberschaubaren Menge von Rückrufen werden. Wenn Sie eine Bibliothek wie async.js verwenden, die eine Reihe von Metaphern bereitstellt, ist das Schreiben von asynchronem Code möglicherweise eine Freude.