Callbackproblemen met Async oplossen

Wanneer we voor het eerst met programmeren beginnen, leren we dat een codeblok van boven naar beneden wordt uitgevoerd. Dit is synchroon programmeren: elke operatie is voltooid voordat de volgende begint. Dit is geweldig als u veel dingen doet die vrijwel geen tijd kost om door een computer te worden voltooid, zoals het toevoegen van getallen, het manipuleren van een tekenreeks of het toewijzen van variabelen. 

Wat gebeurt er wanneer u iets wilt doen dat relatief veel tijd kost, zoals het openen van een bestand op schijf, het verzenden van een netwerkverzoek of wachten tot er een timer verstrijkt? Bij synchroon programmeren kan uw script niets anders doen terwijl het wacht. 

Dit kan prima zijn voor iets eenvoudigs of in een situatie waarin je meerdere exemplaren van je script gebruikt, maar voor veel servertoepassingen is het een nachtmerrie. 

Voer asynchrone programmering in. In een asynchroon script blijft uw code uitvoeren terwijl u wacht tot er iets gebeurt, maar kunt u terug springen wanneer dat iets is gebeurd. 

Neem bijvoorbeeld een netwerkverzoek. Als u een netwerkaanvraag doet naar een langzame server die een volledige drie seconden nodig heeft om te reageren, kan uw script actief andere dingen doen terwijl deze langzame server reageert. In dit geval lijkt drie seconden misschien niet zo erg op een mens, maar een server kan tijdens het wachten op duizenden andere verzoeken reageren. Hoe gaat u om met asynchronie in Node.js? 

De meest eenvoudige manier is via een terugroepactie. Een callback is slechts een functie die wordt aangeroepen wanneer een asynchrone bewerking is voltooid. Volgens afspraak hebben Node.js callback-functies ten minste één argument, dwalen. Terugbellen kan meer argumenten bevatten (die meestal de gegevens vertegenwoordigen die naar de callback worden geretourneerd), maar de eerste zal zijn dwalen. Zoals je misschien al geraden had, dwalen bevat een foutobject (als een fout is getriggerd, daarover later meer).

Laten we een heel eenvoudig voorbeeld bekijken. We zullen de ingebouwde bestandsysteemmodule van Node.js gebruiken (fs). In dit script lezen we de inhoud van een tekstbestand. De laatste regel van het bestand is a console.log dat stelt een vraag: als je dit script uitvoert, denk je dan dat je het logboek zult zien voordat we de inhoud van het tekstbestand zien?

var fs = require ('fs'); fs.readFile ('a-text-file.txt', // de bestandsnaam van een tekstbestand met de tekst 'Hello!' 'utf8', // de codering van het bestand, in dit geval de utf-8-functie (err , tekst) // de callback console.log ('Fout:', err); // Fouten, indien aanwezig console.log ('Tekst:', tekst); // de inhoud van het bestand); // Zal dit vóór of na de fout / tekst zijn? console.log ('Wordt dit voor of na de inhoud van het tekstbestand vastgelegd?'); 

Omdat dit asynchroon is, zien we het laatste console.log voor de inhoud van het tekstbestand. Als u een bestand met de naam hebt a-text-file.txt in dezelfde map waarin u uw knooppuntscript uitvoert, ziet u dat dwalen is nul, en de waarde van tekst wordt gevuld met de inhoud van het tekstbestand. 

Als u geen bestand met de naam hebt a-text-file.txt, dwalen retourneert een fout-object en de waarde van tekst zal zijn onbepaald. Dit leidt tot een belangrijk aspect van callbacks: u moet altijd omgaan met uw fouten. Om fouten af ​​te handelen, moet u een waarde controleren in de dwalenvariable; als er een waarde aanwezig is, is er een fout opgetreden. Bij conventie, dwalen argumenten komen meestal niet terug vals, dus je kunt alleen op echtheid controleren.

var fs = require ('fs'); fs.readFile ('a-text-file.txt', // de bestandsnaam van een tekstbestand met de tekst 'Hello!' 'utf8', // de codering van het bestand, in dit geval de utf-8-functie (err , tekst) // de callback if (err) console.error (err); // toon een fout in de console else console.log ('Tekst:', tekst); // geen fout, dus toon de inhoud van het bestand);

Laten we nu zeggen dat u de inhoud van twee bestanden in een bepaalde volgorde wilt weergeven. Je krijgt zoiets als dit:

var fs = require ('fs'); fs.readFile ('a-text-file.txt', // de bestandsnaam van een tekstbestand met de tekst 'Hello!' 'utf8', // de codering van het bestand, in dit geval de utf-8-functie (err , tekst) // de callback if (err) console.error (err); // geeft een fout weer voor de console else console.log ('Eerste tekstbestand:', tekst); // geen fout, dus toon de inhoud van het bestand fs.readFile ('another-text-file.txt', // de bestandsnaam van een tekstbestand dat zegt "Hello!" 'utf8', // de codering van het bestand, in dit geval , utf-8 functie (err, tekst) // de terugroep indien (err) console.error (err); // toon een fout in de console else console.log ('Tweede tekstbestand:', tekst ); // geen fout, dus geef de inhoud van het bestand weer););

De code ziet er behoorlijk smerig uit en heeft een aantal problemen:

  1. Je laadt de bestanden sequentieel; het zou efficiënter zijn als je ze allebei tegelijkertijd zou kunnen laden en de waarden zou teruggeven als ze allebei volledig geladen zijn.

  2. Syntactisch is het correct maar moeilijk te lezen. Let op het aantal geneste functies en de toenemende tabbladen. Je zou wat trucjes kunnen doen om het een beetje beter te laten lijken, maar je kunt op andere manieren de leesbaarheid opofferen.

  3. Het is niet erg algemeen doel. Dit werkt prima voor twee bestanden, maar wat als je negen bestanden had soms en andere keer 22 of slechts één? De manier waarop het momenteel wordt geschreven is erg rigide.

Maak je geen zorgen, we kunnen al deze problemen (en meer) oplossen met async.js.

Terugbellen met Async.js

Laten we eerst beginnen door de module async.js te installeren.

npm installeer async --save

Async.js kan worden gebruikt om arrays van functies in serie of parallel aan elkaar te lijmen. Laten we ons voorbeeld herschrijven:

var async = require ('async'), //async.js module fs = require ('fs'); async.series (// voer de functies in het eerste argument de een na de ander uit [// Het eerste argument is een array van functies functie (cb) // 'cb' is een afkorting van "callback" fs.readFile ('a- text-file.txt ',' utf8 ', cb);, function (cb) fs.readFile (' another-text-file.txt ',' utf8 ', cb);], function (err, values ) // De "klaar" terugroep die wordt uitgevoerd nadat de functies in de array zijn voltooid als (err) // Als er fouten zijn opgetreden bij het uitvoeren van functies in de array, deze als de err. Console.error ( err); else // Als er een fout is, dan is alles goed console.log ('Eerste tekstbestand:', waarden [0]); console.log ('Tweede tekstbestand:', waarden [1]); );

Dit werkt bijna net als het vorige voorbeeld, sequentieel laden van elk bestand, en verschilt alleen in dat het leest elk bestand en toont het resultaat niet totdat het is voltooid. De code is beknopter en schoner dan het vorige voorbeeld (en we zullen het later nog beter maken). async.series neemt een reeks functies over en voert ze een voor een uit. 

Elke functie zou slechts één enkel argument moeten hebben, de callback (of cb in onze code). cbmoet worden uitgevoerd met hetzelfde type argumenten als elke andere callback, zodat we het recht in onze kunnen plaatsen fs.readFile argumenten. 

Ten slotte worden de resultaten verzonden naar de laatste callback, het tweede argument in naar async.series. De resultaten worden opgeslagen in een array met de waarden die correleren met de volgorde van de functies in het eerste argument van async.series.

Met async.js wordt de foutafhandeling vereenvoudigd omdat als deze een fout tegenkomt, deze de fout retourneert naar het argument van de laatste terugroep en geen verdere asynchrone functies uitvoert. 

Allemaal samen nu

Een gerelateerde functie is async.parallel; het heeft dezelfde argumenten als async.series zodat je tussen de twee kunt wisselen zonder de rest van je syntaxis te veranderen. Dit is een goed punt om parallel versus gelijktijdig te behandelen. 

JavaScript is in feite een taal met één thread, wat betekent dat het maar één ding tegelijk kan doen. Het is in staat om sommige taken uit te voeren in een aparte thread (bijvoorbeeld de meeste I / O-functies), en dit is waar asynchrone programmering in het spel komt met JS. Verwar geen verwarring met samenloop

Wanneer je twee dingen uitvoert met async.parallel, je maakt er geen andere thread voor open om JavaScript te analyseren of twee dingen tegelijkertijd te doen - je controleert echt wanneer het tussen functies gaat in het eerste argument van async.parallel. Dus je wint niets door alleen maar synchrone code in async.parallel te zetten. 

Dit wordt het best visueel uitgelegd:

Hier is ons vorige voorbeeld geschreven om parallel te zijn - het enige verschil is dat we gebruiken async.parallel liever dan async.series.

var async = require ('async'), //async.js module fs = require ('fs'); async.parallel (// voer de functies in het eerste argument uit, maar wacht niet tot de eerste functie is voltooid om de tweede te starten [// Het eerste argument is een array met functies functie (cb) // 'cb' is steno voor "callback" fs.readFile ('a-text-file.txt', 'utf8', cb);, function (cb) fs.readFile ('another-text-file.txt', 'utf8 ', cb);], functie (err, waarden) // De' klaar'-callback die wordt uitgevoerd nadat de functies in de array zijn voltooid als (err) // Als er fouten zijn opgetreden wanneer functies in de array zijn uitgevoerd , ze zullen worden verzonden als de err. console.error (err); else // Als err is vals, dan is alles goed console.log ('Eerste tekstbestand:', waarden [0]); console.log ( 'Tweede tekstbestand:', waarden [1]););

Opnieuw en opnieuw

Onze vorige voorbeelden hebben een vast aantal bewerkingen uitgevoerd, maar wat gebeurt er als u een variabel aantal asynchrone bewerkingen nodig hebt? Dit wordt snel rommelig als u alleen maar op callbacks en reguliere taalconstructies vertrouwt, vertrouwend op onhandige items of voorwaardecontroles die de werkelijke betekenis van uw code verdoezelen. Laten we eens kijken naar het ruwe equivalent van een for-lus met async.js.

In dit voorbeeld schrijven we tien bestanden naar de huidige map met sequentiële bestandsnamen en enkele korte inhoud. U kunt het aantal variëren door de waarde van het eerste argument van te wijzigen async.times. In dit voorbeeld is de callback voor fs.writeFile maakt alleen een dwalen argument, maar de async.times functie kan ook een retourwaarde ondersteunen. Net als async.series wordt het als een array doorgegeven aan de voltooide callback in het tweede argument.

var async = vereisen ('async'), fs = require ('fs'); async.times (10, // aantal keren dat de functie wordt uitgevoerd (runCount, callback) fs.writeFile ('file -' + runCount + '. txt', // de nieuwe bestandsnaam 'This is file number' + runCount, // de inhoud van de nieuwe bestands callback);, function (err) if (err) console.error (err); else console.log ('Wrote files.';);

Het is een goed moment om te zeggen dat de meeste async.js-functies standaard parallel lopen in plaats van series. In het bovenstaande voorbeeld begint het dus de bestanden te maken en te rapporteren wanneer ze allemaal volledig zijn gemaakt en geschreven. 

Die functies die standaard parallel lopen hebben een bijbehorende reeksfunctie, aangegeven door de functie eindigend met, je raadt het al, 'Series'. Dus als je dit voorbeeld in serie wilt laten lopen in plaats van parallel, zou je veranderen async.times naar async.timesSeries.

Voor ons volgende voorbeeld van looping zullen we de functie async.until bekijken. async.until voert een asynchrone functie uit (in serie) tot aan een specifieke voorwaarde is voldaan. Deze functie heeft drie functies als argumenten. 

De eerste functie is de test waarbij u true retourneert (als u de lus wilt stoppen) of false (als u de lus wilt voortzetten). Het tweede argument is de asynchrone functie en de finale is de voltooide callback. Bekijk dit voorbeeld:

var async = require ('async'), fs = require ('fs'), startTime = new Date (). getTime (), // unix timestamp in milliseconds runCount = 0; async.until (function () // return true als 4 milliseconden zijn verstreken, anders false (en doorgaan met het uitvoeren van het script) retourneert de nieuwe datum (). getTime ()> (startTime + 5);, function (callback)  runCount + = 1; fs.writeFile ('getimede bestand -' + runCount + '. txt', // de nieuwe bestandsnaam 'Dit is bestandsnummer' + runCount, // de inhoud van de nieuwe bestands callback);, function (err) if (err) console.error (err); else console.log ('Wrote files.';);

Dit script zal gedurende vijf milliseconden nieuwe tekstbestanden creëren. Aan het begin van het script krijgen we de begintijd in het unix-tijdperk van milliseconden en in de testfunctie krijgen we de huidige tijd en test om te zien of het vijf milliseconden groter is dan de starttijd plus vijf. Als u dit script meerdere keren uitvoert, krijgt u mogelijk andere resultaten. 

Op mijn computer maakte ik tussen de 5 en 20 bestanden in vijf milliseconden. Interessant, als je probeert toe te voegen console.log in de testfunctie of de asynchrone functie, je krijgt heel verschillende resultaten omdat het tijd kost om naar je console te schrijven. Het laat alleen maar zien dat in software alles een prestatiekost heeft!

De voor elke lus is een handige structuur - hiermee kunt u iets doen voor elk item van een array. In async.js zou dit de async.each functie. Voor deze functie zijn drie argumenten nodig: de verzameling of array, de asynchrone functie die moet worden uitgevoerd voor elk item en de voltooide callback. 

In het onderstaande voorbeeld nemen we een reeks tekenreeksen (in dit geval typen windhondenrassen) en maken we voor elke reeks een bestand. Wanneer alle bestanden zijn gemaakt, wordt de callback uitgevoerd. Zoals je zou verwachten, worden fouten afgehandeld via de dwalen object in de callback. async.each wordt parallel uitgevoerd, maar als u het in serie wilt uitvoeren, kunt u het eerder genoemde patroon volgen en gebruiken async.eachSeries in plaats van async.each.

var async = vereisen ('async'), fs = require ('fs'); async.each (// een reeks windhondenrassen ['windhond', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italian-greyhound'], functie ( dogBreed, callback) fs.writeFile (dogBreed + '. txt', // de nieuwe bestandsnaam 'file voor honden van het ras' + dogBreed, // de inhoud van de nieuwe bestands callback);, function (err)  if (err) console.error (err); else console.log ('Klaar met het schrijven van bestanden over honden.'););

Een neef van async.each is de async.map functie; het verschil is dat je de waarden kunt terugsturen naar je callback. Met de async.map functie, u geeft een array of verzameling door als het eerste argument en vervolgens wordt een asynchrone functie uitgevoerd op elk item in de array of verzameling. Het laatste argument is de voltooide callback. 

Het onderstaande voorbeeld neemt de reeks hondenrassen en gebruikt elk item om een ​​bestandsnaam te maken. De bestandsnaam wordt vervolgens doorgegeven aan fs.readFile, waar het wordt gelezen en de waarden worden teruggegeven door de callback-functie. Je eindigt met een array van de inhoud van het bestand in de gedaan callback-argumenten.

var async = vereisen ('async'), fs = require ('fs'); async.map (['greyhound', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italian-greyhound'], function (dogBreed, callback) fs.readFile (dogBreed + '. txt', // de nieuwe bestandsnaam 'utf8', callback);, function (err, dogBreedFileContents) if (err) console.error (err); else console.log ('dog rassen '); console.log (dogBreedFileContents););

async.filter is ook qua syntaxis vergelijkbaar met async.each en async.map, maar met filter verzendt u een Booleaanse waarde naar de item-callback in plaats van de waarde van het bestand. In de voltooide callback krijg je een nieuwe array, met alleen de elementen die je gepasseerd a waar of waarheidswaarde voor in het item terugbellen. 

var async = vereisen ('async'), fs = require ('fs'); async.filter (['greyhound', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italian-greyhound'], function (dogBreed, callback) fs.readFile (dogBreed + '. txt', // de nieuwe bestandsnaam 'utf8', functie (err, fileContents) if (err) callback (err); else callback (err, // dit zal vals zijn sinds we hebben gecontroleerd it above fileContents.match (/ greyhound / gi) // gebruik RegExp om te controleren of de string 'windhond' in de inhoud van het bestand voorkomt););, function (err, dogBreedFileContents) if (err) console .error (err); else console.log ('greyhound breeds:'); console.log (dogBreedFileContents););

In dit voorbeeld doen we nog een paar dingen anders dan in de vorige voorbeelden. Merk op hoe we een extra functieaanroep toevoegen en onze eigen fout verwerken. De als dwalen en callback (err) patroon is erg handig als je de resultaten van een asynchrone functie moet manipuleren, maar je wilt async.js nog steeds de fouten laten verwerken. 

Bovendien zul je merken dat we de err-variabele gebruiken als het eerste argument voor de callback-functie. Op het eerste gezicht ziet dit er niet helemaal goed uit. Maar omdat we al hebben gecontroleerd op de waarheidsgetrouwheid van fouten, weten we dat het vals en veilig is om door te geven aan de callback. 

Over de rand van een klif

Tot dusverre hebben we een aantal bruikbare bouwstenen verkend die ruwe synoniemen hebben in synchrone programmering. Laten we recht naar binnen duiken async.waterfall, wat niet veel van een equivalent in de synchrone wereld heeft. 

Het concept met een waterval is dat de resultaten van één asynchrone functie in de argumenten van een andere asynchrone functie in serie vloeien. Het is een zeer krachtig concept, vooral wanneer u probeert meerdere asynchrone functies met elkaar te verbinden die op elkaar zijn gebaseerd. Met async.waterfall, het eerste argument is een array van functies, en het tweede argument is je callback. 

In uw array van functies begint de eerste functie altijd met een enkel argument, de callback. Elke volgende functie moet overeenkomen met de niet-err-argumenten van de vorige functie zonder de fout-functie en met de toevoeging van de nieuwe callback.

In ons volgende voorbeeld beginnen we enkele concepten te combineren met waterval als a lijm. In de array die het eerste argument is, hebben we drie functies: de eerste laadt de directorylijst uit de huidige directory, de tweede neemt de directoryaanbieding en gebruikt async.map rennen fs.stat op elk bestand en de derde functie neemt de directorylijst van het eerste functieresultaat en krijgt de inhoud voor elk bestand (fs.readFile).

async.waterfall voert elke functie sequentieel uit, dus deze zal altijd alle fs.stat functies voordat er een wordt uitgevoerd fs.readFile. In dit eerste voorbeeld zijn de tweede en derde functie niet afhankelijk van elkaar, zodat ze kunnen worden ingepakt in een async.parallel om de totale uitvoeringstijd te verkorten, maar we zullen deze structuur opnieuw aanpassen voor het volgende voorbeeld.

Notitie: Voer dit voorbeeld uit in een kleine map met tekstbestanden, anders krijg je veel vuilnis voor een lange tijd in je terminalvenster.

var async = vereisen ('async'), fs = require ('fs'); async.waterfall ([function (callback) fs.readdir ('.', callback); // lees de huidige directory, geef deze door aan de volgende functie., function (bestandsnamen, callback) // 'fileNames' is de directorylijst van de vorige functie async.map (bestandsnamen, // De directorylijst is slechts een array van bestandsnamen, fs.stat, // zodat we async.map kunnen gebruiken om fs.stat uit te voeren voor elke bestandsnaamfunctie (err , stats) if (err) callback (err); else callback (err, fileNames, stats); // geef de foutmelding, de directory-lijst en de stat-verzameling door aan het volgende item in de waterval) ;, functie (bestandsnamen, statistieken, callback) // de directorylijst, 'bestandsnamen' wordt vergezeld door de verzameling fs.stat-objecten in 'stats' async.map (bestandsnamen, functie (aFileName, readCallback) // Deze keer nemen we de bestandsnamen met de kaart en geven deze door aan fs.readFile om de inhoud te krijgen fs.readFile (aFileName, 'utf8', readCallback);, functie (err, inhoud) if (err) callback (err); else // Now our callback w ziek zijn drie argumenten, de oorspronkelijke directorylijst ('bestandsnamen'), de verzameling fs.stats en een array met de inhoud van elk bestand callback (err, bestandsnamen, statistieken, inhoud); ); ], functie (err, bestandsnamen, statistieken, inhoud) if (err) console.error (err);  else console.log (bestandsnamen); console.log (statistieken); console.log (inhoud); );

Laten we zeggen dat we de resultaten willen hebben van alleen de bestanden met een grootte van meer dan 500 bytes. We kunnen de bovenstaande code gebruiken, maar je krijgt de grootte en inhoud van elk bestand, of je het nu nodig hebt of niet. Hoe kon je gewoon de stat van de bestanden krijgen en alleen de inhoud van de bestanden die aan de vereisten voor de grootte voldoen? 

Ten eerste kunnen we alle anonieme functies naar genoemde functies slepen. Het is persoonlijke voorkeur, maar het maakt de code een beetje schoner en gemakkelijker te begrijpen (herbruikbaar om op te starten). Zoals je je misschien wel kunt voorstellen, moet je de formaten ophalen, die formaten evalueren en alleen de inhoud van de bestanden ophalen die groter is dan de vereiste grootte. Dit kan eenvoudig worden bereikt met iets als Array.filter, maar dat is een synchrone functie en async.waterfall verwacht functies in asynchrone stijl. Async.js heeft een helperfunctie die synchrone functies in asynchrone functies kan omsluiten, de nogal jazzy-functies async.asyncify.

We moeten drie dingen doen, die we allemaal inpakken async.asyncify. Eerst nemen we de bestandsnaam en de statistische arrays van de arrayFsStat functie, en we voegen ze samen met kaart. Vervolgens filteren we alle items met een statistiek van minder dan 300. Ten slotte nemen we de gecombineerde bestandsnaam en stat-object en gebruiken kaart om de bestandsnaam er gewoon uit te halen. 

Nadat we de namen van de bestanden met een grootte van minder dan 300 hebben, gebruiken we async.map en fs.readFile om de inhoud te krijgen. Er zijn veel manieren om dit ei te kraken, maar in ons geval is het gebroken om maximale flexibiliteit en hergebruik van code te tonen. Deze async.waterfall gebruik illustreert hoe u synchrone en asynchrone code kunt mixen en matchen.

var async = vereisen ('async'), fs = require ('fs'); // Onze anonieme refactored in genoemde functies functie directoryListing (callback) fs.readdir ('.', Callback);  function arrayFsStat (bestandsnamen, callback) async.map (bestandsnamen, fs.stat, functie (err, stats) if (err) callback (err); else callback (err, fileNames, stats); );  function arrayFsReadFile (bestandsnamen, callback) async.map (bestandsnamen, functie (aFileName, readCallback) fs.readFile (aFileName, 'utf8', readCallback);, function (err, contents) if (err) callback (err); else callback (err, contents););  // Deze functies zijn synchrone functie mergeFilenameAndStat (bestandsnamen, statistieken) return stats.map (functie (aStatObj, index) aStatObj.fileName = bestandsnamen [index]; return aStatObj;);  function above300 (combinedFilenamesAndStats) return combinedFilenamesAndStats .filter (function (aStatObj) return aStatObj.size> = 300;);  function justFilenames (combinedFilenamesAndStats) return combinedFilenamesAndStats .map (function (aCombinedFileNameAndStatObj) return aCombinedFileNameAndStatObj.fileName;);  async.waterfall ([directoryLists, arrayFsStat, async.asyncify (mergeFilenameAndStat), // asyncify omhult synchrone functies in een err-first callback async.asyncify (above300), async.asyncify (justFilesames), arrayFsReadFile], function (err, inhoud) if (err) console.error (err); else console.log (contents););

Laten we een stap verder gaan en onze functie nog verder verfijnen. Laten we zeggen dat we een functie willen schrijven die precies zo werkt als hierboven, maar met de flexibiliteit om op elk pad te kijken. Een hechte neef van async.waterfall is async.seq. Terwijl async.waterfall voert gewoon een waterval aan functies uit, async.seq geeft een functie terug die een waterval van andere functies uitvoert. Naast het maken van een functie, kunt u waarden doorgeven die naar de eerste asynchrone functie gaan. 

Converteren naar async.seq duurt slechts enkele wijzigingen. Eerst zullen we wijzigen directoryListing om een ​​argument te accepteren - dit zal het pad zijn. Ten tweede voegen we een variabele toe om onze nieuwe functie te behouden (directoryAbove300). Ten derde nemen we het array-argument van de async.waterfall en vertaal dat in argumenten voor async.seq. Ons gedaan terugbellen voor de waterval wordt nu gebruikt als het gedaan terugbellen wanneer we rennen directoryAbove300.

var async = require ('async'), fs = require ('fs'), directoryAbove300; functie directoryListing (initialPath, callback) // we kunnen een variabele doorgeven aan de eerste functie die wordt gebruikt in async.seq - de resulterende functie kan argumenten accepteren en deze eerste functie fs.readdir doorgeven (initialPath, callback);  function arrayFsStat (bestandsnamen, callback) async.map (bestandsnamen, fs.stat, functie (err, stats) if (err) callback (err); else callback (err, fileNames, stats); );  function arrayFsReadFile (bestandsnamen, callback) async.map (bestandsnamen, functie (aFileName, readCallback) fs.readFile (aFileName, 'utf8', readCallback);, function (err, contents) if (err) callback (err); else callback (err, contents););  functie mergeFilenameAndStat (bestandsnamen, statistieken) return stats.map (functie (aStatObj, index) aStatObj.fileName = bestandsnamen [index]; return aStatObj;);  function above300 (combinedFilenamesAndStats) return combinedFilenamesAndStats .filter (function (aStatObj) return aStatObj.size> = 300;);  function justFilenames (combinedFilenamesAndStats) return combinedFilenamesAndStats .map (function (aCombinedFileNameAndStatObj) return aCombinedFileNameAndStatObj.fileName;) //async.seq zal een nieuwe functie produceren die je steeds opnieuw kunt gebruiken directoryAbove300 = async.seq (directoryLists, arrayFsStat, async.asyncify (mergeFilenameAndStat), async.asyncify (above300), async.asyncify (justFilesames), arrayFsReadFile); directoryAbove300 ('.', functie (err, bestandsnamen, statistieken, inhoud) if (err) console.error (err); else console.log (bestandsnamen););

Een opmerking over beloften en async-functies

U vraagt ​​zich misschien af ​​waarom ik beloften niet heb genoemd. Ik heb niets tegen hen - ze zijn best handig en misschien een elegantere oplossing dan callbacks - maar ze zijn een andere manier om te kijken naar asynchrone codering. 

Ingebouwde Node.js-modules gebruiken dwalen-eerste callbacks en duizenden andere modules gebruiken dit patroon. Daarom gebruikt deze zelfstudie in feite fs in de voorbeelden - iets dat zo fundamenteel is als de toegang tot bestandssystemen in Node.js gebruikt callbacks, dus het ruzie maken van callbackcodes zonder beloftes is een essentieel onderdeel van Node.js-programmering.  

Het is mogelijk om zoiets als Bluebird te gebruiken om err-first callbacks in Promise-gebaseerde functies te verwerken, maar dat brengt je alleen maar tot nu toe - Async.js biedt een groot aantal metaforen die asynchrone code leesbaar en beheersbaar maken.

Omarm Asynchrony

JavaScript is een van de de facto talen geworden om op het web te werken. Het is niet zonder zijn leercurven, en er zijn ook genoeg kaders en bibliotheken om je bezig te houden. Als u op zoek bent naar extra bronnen om te studeren of te gebruiken in uw werk, kijk dan wat we beschikbaar hebben op de Envato-marktplaats.

Maar asynchroon leren is iets heel anders, en hopelijk heeft deze tutorial je laten zien hoe nuttig het kan zijn.

Asynchronie is de sleutel tot het schrijven van JavaScript aan de serverzijde, maar als het niet goed is ontworpen, kan uw code een onbeheersbaar beest van callbacks worden. Door een bibliotheek zoals async.js te gebruiken die een aantal metaforen biedt, zou je kunnen merken dat het schrijven van asynchrone code een vreugde is.