Løse Tilbakeringingsproblemer Med Async

Når vi først starter programmering, lærer vi at en blokk med kode kjøres fra topp til bunn. Dette er synkron programmering: hver operasjon er fullført før neste begynner. Dette er flott når du gjør mange ting som tar praktisk talt ingen tid for en datamaskin å fullføre, for eksempel å legge til tall, manipulere en streng eller tilordne variabler. 

Hva skjer når du vil gjøre noe som tar relativt lang tid, for eksempel å få tilgang til en fil på disk, sende en nettverksforespørsel, eller vente på at en timer skal gå? I synkron programmering kan skriptet ikke gjøre noe annet mens det venter. 

Dette kan være bra for noe enkelt eller i en situasjon der du har flere forekomster av skriptet ditt, men for mange serverprogrammer er det et mareritt. 

Angi asynkron programmering. I et asynkront skript fortsetter koden din å kjøre mens du venter på at noe skal skje, men kan hoppe tilbake når det har skjedd noe. 

Ta for eksempel en nettverksforespørsel. Hvis du gjør en nettverksforespørsel til en treg server som tar fullt tre sekunder å svare, kan skriptet aktivt gjøre andre ting mens denne tregte serveren svarer. I dette tilfellet kan det hende at tre sekunder ikke virker så mye for et menneske, men en server kan svare på tusenvis av andre forespørsler mens de venter. Så, hvordan håndterer du asynkroni i Node.js? 

Den mest grunnleggende måten er gjennom en tilbakeringing. En tilbakeringing er bare en funksjon som kalles når en asynkron operasjon er fullført. Ved konvensjon har Node.js tilbakeringingsfunksjoner minst ett argument, err. Tilbakeringinger kan ha flere argumenter (som vanligvis representerer dataene som er returnert til tilbakeringingen), men den første vil være err. Som du kanskje har gjettet, err har et feilobjekt (hvis en feil har blitt utløst-mer på det senere).

La oss ta en titt på et veldig enkelt eksempel. Vi bruker Node.js innebygde filsystemmodul (fs). I dette skriptet leser vi innholdet i en tekstfil. Den siste linjen i filen er en console.log Det stiller et spørsmål: Hvis du kjører dette skriptet, tror du at du vil se loggen før vi ser innholdet i tekstfilen?

var fs = krever ('fs'); fs.readFile ('a-text-file.txt', // filnavnet til en tekstfil som sier "Hei!" 'utf8', // kodingen av filen, i dette tilfellet utf-8-funksjonen (feil , tekst) // tilbakekallings-konsollen.logg ('Feil:', feil); // Feil, hvis noen console.log ('Text:', tekst); // innholdet i filen); // Vil dette være før eller etter feilen / teksten? console.log ('Blir dette logget før eller etter innholdet i tekstfilen?'); 

Siden dette er asynkron, ser vi faktisk sist console.log før innholdet i tekstfilen. Hvis du har en fil som heter a-tekst-file.txt i samme katalog der du kjører nodeskriptet ditt, ser du det err er null, og verdien av tekst er fylt med innholdet i tekstfilen. 

Hvis du ikke har en fil som heter a-tekst-file.txt, err vil returnere et feilobjekt, og verdien av tekst vil være udefinert. Dette fører til et viktig aspekt av tilbakeringinger: du må alltid håndtere feilene dine. For å håndtere feil må du sjekke en verdi i errvariabel; Hvis en verdi er til stede, oppsto en feil. Etter konvensjon, err Argumenter returnerer vanligvis ikke falsk, så du kan sjekke bare for truthiness.

var fs = krever ('fs'); fs.readFile ('a-text-file.txt', // filnavnet til en tekstfil som sier "Hei!" 'utf8', // kodingen av filen, i dette tilfellet utf-8-funksjonen (feil , tekst) // tilbakekallingen hvis (err) console.error (err); // viser en feil til konsollen annet console.log ('Text:', tekst); // ingen feil, så vis innholdet i filen);

La oss si at du vil vise innholdet i to filer i en bestemt rekkefølge. Du vil ende opp med noe slikt:

var fs = krever ('fs'); fs.readFile ('a-text-file.txt', // filnavnet til en tekstfil som sier "Hei!" 'utf8', // kodingen av filen, i dette tilfellet utf-8-funksjonen (feil , tekst) // tilbakeringingen hvis (err) console.error (err); // viser en feil til konsollen annet console.log ('Første tekstfil:', tekst); // ingen feil, så vis innholdet i filen fs.readFile ('another-text-file.txt', // filnavnet til en tekstfil som sier "Hei!" 'utf8', // kodingen av filen, i dette tilfellet , utf-8 funksjon (feil, tekst) // tilbakeringingen hvis (feil) console.error (err); // viser en feil til konsollen annet console.log ('Second text file:', text ); // ingen feil, så vis innholdet i filen););

Koden ser ganske ekkel ut og har en rekke problemer:

  1. Du laster filene sekvensielt; det ville være mer effektivt hvis du kunne laste dem begge samtidig og returnere verdiene når begge har lastet helt.

  2. Synaktisk er det riktig, men vanskelig å lese. Legg merke til antall nestede funksjoner og de økende fanene. Du kan gjøre noen triks for å få det til å se litt bedre ut, men du kan ofre lesbarhet på andre måter.

  3. Det er ikke veldig generelt formål. Dette fungerer fint for to filer, men hva om du hadde ni filer noen ganger og andre ganger 22 eller bare en? Måten den er skrevet for, er veldig stiv.

Ikke bekymre deg, vi kan løse alle disse problemene (og mer) med async.js.

Tilbakeringinger med Async.js

Først, la oss begynne med å installere async.js-modulen.

npm installere async - save

Async.js kan brukes til å lim sammen arrays av funksjoner i enten serie eller parallell. La oss omskrive vårt eksempel:

var async = krever ('async'), //async.js modul fs = krever ('fs'); async.series (// utfører funksjonene i det første argumentet etter hverandre [// Det første argumentet er en rekke funksjonerfunksjoner (cb) // 'cb' er stenografi for "tilbakeringing" fs.readFile ('a- tekst-fil.txt ',' utf8 ', cb);, funksjon (cb) fs.readFile (' another-text-file.txt ',' utf8 ', cb);], funksjon ) // Den "ferdige" tilbakeringingen som kjøres etter at funksjonene i arrayet har fullført hvis (err) // Hvis det oppstod feil når funksjonene i arrayet ble utført, vil de bli sendt som feilkonsollen.error ( err); else // Hvis feil er falsk, så er alt bra konsoll.log ('Første tekstfil:', verdier [0]); console.log ('Second text file:', values ​​[1]); );

Dette fungerer nesten som det forrige eksempelet, og laster hver fil i rekkefølge, og adskiller seg bare ved at den leser hver fil og ikke viser resultatet før det er fullført. Koden er mer konsis og renere enn forrige eksempel (og vi vil gjøre det enda bedre senere). async.series tar en rekke funksjoner og utfører dem en etter en. 

Hver funksjon bør bare ha et enkelt argument, tilbakeringingen (eller cb i vår kode). cbbør utføres med samme type argumenter som enhver annen tilbakeringing, så vi kan sette det rett inn i vår fs.readFile argumenter. 

Til slutt blir resultatene sendt til den endelige tilbakeringingen, det andre argumentet i til async.series. Resultatene lagres i en matrise med verdiene som korrelerer med rekkefølgen av funksjonene i det første argumentet til async.series.

Med async.js forenkles feilsøkingen fordi hvis den opplever en feil, returnerer den feilen til argumentet for den endelige tilbakeringingen og vil ikke utføre noen videre asynkrone funksjoner. 

Alle sammen nå

En relatert funksjon er async.parallel; Den har de samme argumentene som async.series slik at du kan bytte mellom de to uten å endre resten av syntaksen din. Dette er et godt poeng å dekke parallelt versus samtidig. 

JavaScript er i utgangspunktet et gjenget språk, noe som betyr at det bare kan gjøre én ting av gangen. Det er i stand til å gjøre noen oppgaver i en egen tråd (de fleste I / O-funksjoner, for eksempel), og dette er der asynkron programmering kommer til spill med JS. Ikke forveksle parallell med samtidighet

Når du utfører to ting med async.parallel, du gjør det ikke åpne en annen tråd for å analysere JavaScript eller gjøre to ting om gangen - du styrer virkelig når den går mellom funksjoner i det første argumentet om async.parallel. Så du får ikke noe ved å bare sette synkron kode i async.parallel. 

Dette er best forklart visuelt:

Her er vårt tidligere eksempel skrevet for å være parallelt-den eneste forskjellen er det vi bruker async.parallel heller enn async.series.

var async = krever ('async'), //async.js modul fs = krever ('fs'); async.parallel (// utfør funksjonene i det første argumentet, men vent ikke på den første funksjonen til slutt for å starte det andre [// Det første argumentet er en rekke funksjonerfunksjoner (cb) // 'cb' er shorthand for "tilbakeringing" fs.readFile ('a-text-file.txt', 'utf8', cb);, funksjon (cb) fs.readFile ('another-text-file.txt', 'utf8 ', cb);], funksjon (feil, verdier) // Den "ferdige" tilbakeringingen som kjøres etter at funksjonene i arrayet har fullført hvis (err) // Hvis det oppsto noen feil når funksjoner i arrayet ble utført , vil de bli sendt som feil. console.error (err); else else // Hvis feil er falsk, så er alt bra konsoll.log ('Første tekstfil:', verdier [0]); console.log 'Andre tekstfil:', verdier [1]););

Igjen og igjen

Våre tidligere eksempler har utført et fast antall operasjoner, men hva skjer hvis du trenger et variabelt antall asynkrone operasjoner? Dette blir rotete raskt hvis du bare stoler på tilbakeringinger og vanlig språkkonstruksjon, avhengig av klumpete tellere eller tilstandskontroller som skjuler den virkelige betydningen av koden din. La oss ta en titt på det grove ekvivalentet av en for loop med async.js.

I dette eksemplet skriver vi ti filer til gjeldende katalog med sekvensielle filnavn og noen korte innhold. Du kan variere tallet ved å endre verdien av det første argumentet til async.times. I dette eksemplet tilbakekallingen for fs.writeFile skaper bare en err argument, men async.times funksjonen kan også støtte en returverdi. Som async.series, blir det sendt til det tilbakekallede tilbakekallingen i det andre argumentet som en matrise.

var async = krever ('async'), fs = krever ('fs'); async.times (10, // antall ganger for å kjøre funksjonsfunksjonen (runCount, tilbakeringing) fs.writeFile ('file -' + runCount + '. txt', // det nye filnavnet 'Dette er filnummer' + runCount, // innholdet i den nye fil tilbakeringingen);, funksjon (err) hvis (err) console.error (err); else console.log ('Skrevne filer.'););

Det er en god tid å si at de fleste async.js funksjoner, som standard, går parallelt i stedet for serier. Så i eksempelet ovenfor vil det begynne å lage filene og rapportere når alle er helt opprettet og skrevet. 

De funksjonene som kjører parallelt som standard har en tilhørende seriefunksjon indikert av funksjonen som slutter med, du gjettet det, 'Serie'. Så hvis du ønsket å kjøre dette eksemplet i serie i stedet for parallell, ville du endre async.times til async.timesSeries.

For vårt neste eksempel på looping, tar vi en titt på async.until-funksjonen. async.until utfører en asynkron funksjon (i serie) til en bestemt tilstand er oppfylt. Denne funksjonen tar tre funksjoner som argumenter. 

Den første funksjonen er testen hvor du returnerer enten sant (hvis du vil stoppe sløyfen) eller falsk (hvis du vil fortsette sløyfen). Det andre argumentet er den asynkrone funksjonen, og den endelige er den tilbakekallede tilbakekallingen. Ta en titt på dette eksemplet:

var async = krever ('async'), fs = krever ('fs'), startTime = ny dato (). getTime (), // unix tidsstempel i millisekunder runCount = 0; async.until (function () // return true hvis 4 millisekunder er gått, ellers falsk (og fortsett å kjøre skriptet) returnere nytt Date (). getTime ()> (startTime + 5);, funksjon (tilbakeringing)  runCount + = 1; fs.writeFile ('tidsbestemt fil -' + runCount + '. txt', // det nye filnavnet 'Dette er filnummer' + runCount, // innholdet i den nye filoppringningen);, funksjon (err) hvis (err) console.error (err); else console.log ('Skrevne filer.'););

Dette skriptet vil skape nye tekstfiler i fem millisekunder. Ved starten av skriptet får vi starttiden i millisekundens unix-epoke, og i testfunksjonen får vi nåværende tid og test for å se om det er fem millisekunder større enn starttiden pluss fem. Hvis du kjører dette skriptet flere ganger, kan du få forskjellige resultater. 

På min maskin skapte jeg mellom 6 og 20 filer i fem millisekunder. Interessant, hvis du prøver å legge til console.log i enten testfunksjonen eller den asynkrone funksjonen, får du svært forskjellige resultater fordi det tar tid å skrive til konsollen. Det går bare for å vise deg at i programvare har alt en ytelseskost!

For hver sløyfe er en praktisk struktur - det lar deg gjøre noe for hvert element i en matrise. I async.js vil dette være async.each funksjon. Denne funksjonen tar tre argumenter: samlingen eller arrayen, den asynkrone funksjonen som skal utføres for hvert element, og det tilbakekallede tilbakekallingen. 

I eksemplet nedenfor tar vi en rekke strenger (i dette tilfellet typer sighthound raser) og lager en fil for hver streng. Når alle filene er opprettet, utføres den tilbakekalte tilbakeringingen. Som du kanskje regner med, håndteres feil via err objekt i den tilbakekalte tilbakekallingen. async.each kjøres parallelt, men hvis du vil kjøre den i serie, kan du følge det tidligere nevnte mønsteret og bruke async.eachSeries i stedet for async.each.

var async = krever ('async'), fs = krever ('fs'); async.each (// et utvalg av sighthound hunderaser ['greyhound', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italian-greyhound'], funksjon dogBreed, callback) fs.writeFile (dogBreed + '.txt', // det nye filnavnet 'fil for hunder av rasen' + dogBreed, // innholdet i den nye fil tilbakeringingen);, funksjon (err)  hvis (err) console.error (err); else console.log ('Ferdig skriving av filer om hunder.'););

En fetter av async.each er den async.map funksjon; Forskjellen er at du kan sende verdiene tilbake til ditt tilbakekalling. Med async.map funksjon, passerer du i en matrise eller samling som det første argumentet, og deretter blir en asynkron funksjon kjørt på hvert element i samlingen eller samlingen. Det siste argumentet er det tilbakekallede tilbakemeldingen. 

Eksempelet nedenfor tar et utvalg av hunderaser og bruker hvert element til å opprette et filnavn. Filnavnet blir deretter overført til fs.readFile, hvor det er lest og verdiene sendes tilbake av tilbakeringingsfunksjonen. Du ender med en rekke filinnhold i de ferdige tilbakekallingsargumentene.

var async = krever ('async'), fs = krever ('fs'); async.map (['greyhound', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italian-greyhound'], funksjon (dogBreed, tilbakeringing) fs.readFile (dogBreed + '. txt', // det nye filnavnet 'utf8', tilbakeringing);, funksjon (feil, dogBreedFileContents) hvis (err) console.error (err); else console.log raser '); console.log (dogBreedFileContents););

async.filter er også veldig lik syntax til async.each og async.map, men med filter sender du en boolsk verdi til elementet tilbakeringing i stedet for verdien av filen. I det tilbakekalte tilbakemeldingen får du et nytt utvalg, med bare de elementene du har bestått en ekte eller truthy verdi for i tilbakekallingen. 

var async = krever ('async'), fs = krever ('fs'); async.filter ('greyhound', 'saluki', 'borzoi', 'galga', 'podenco', 'whippet', 'lurcher', 'italian-greyhound'], funksjon (dogBreed, tilbakeringing) fs.readFile (dogBreed + '.txt', // det nye filnavnet 'utf8', funksjon (feil, fileContents) hvis (err) callback (err); else callback (feil, // dette vil være falskt siden vi sjekket det over fileContents.match (/ greyhound / gi) // bruk RegExp for å se etter strengen 'greyhound' i innholdet i filen););, funksjon (feil, dogBreedFileContents) hvis (err) konsoll .error (err); else console.log ('greyhound raser:'); console.log (dogBreedFileContents););

I dette eksemplet gjør vi noen flere ting enn i de tidligere eksemplene. Legg merke til hvordan vi legger til et ekstra funksjonsanrop og håndterer vår egen feil. De hvis err og tilbakeringing (err) mønster er veldig nyttig hvis du trenger å manipulere resultatene av en asynkron funksjon, men du vil fortsatt la async.js håndtere feilene. 

I tillegg vil du legge merke til at vi bruker feilvariabelen som det første argumentet til tilbakeringingsfunksjonen. Ved første blush ser dette ikke helt riktig ut. Men siden vi allerede har sjekket for sannheten om feil, vet vi at det er falskt og trygt å videresende til tilbakekallingen. 

Over kanten av en klippe

Så langt har vi utforsket en rekke nyttige byggesteiner som har grove korollarier i synkron programmering. La oss dykke rett inn async.waterfall, som ikke har mye tilsvarende i synkron verden. 

Konseptet med en foss er at resultatene av en asynkron funksjon flyter inn i argumentene til en annen asynkron funksjon i serie. Det er et veldig kraftig konsept, spesielt når du prøver å stryke sammen flere asynkrone funksjoner som stole på hverandre. Med async.waterfall, Det første argumentet er en rekke funksjoner, og det andre argumentet er din tilbakekalling. 

I ditt utvalg av funksjoner starter den første funksjonen alltid med et enkelt argument, tilbakekallingen. Hver etterfølgende funksjon bør samsvare med ikke-feil-argumentene fra den forrige funksjonen, og err-funksjonen og med tillegg av den nye tilbakekallingen.

I vårt neste eksempel begynner vi å kombinere noen begreper ved å bruke foss som en lim. I arrayet som er det første argumentet, har vi tre funksjoner: den første laster katalogoppføringen fra gjeldende katalog, den andre tar katalogoppføringen og bruker async.map å løpe fs.stat på hver fil, og den tredje funksjonen tar katalogoppføringen fra det første funksjonsresultatet og får innholdet for hver fil (fs.readFile).

async.waterfall Kjører hver funksjon i rekkefølge, så det vil alltid løpe alle fs.stat Fungerer før du kjører noen fs.readFile. I dette første eksempelet er de andre og tredje funksjonene ikke avhengige av hverandre, slik at de kan pakkes inn i en async.parallel for å redusere total kjøretid, men vi endrer denne strukturen på nytt for neste eksempel.

Merk: Kjør dette eksemplet i en liten katalog med tekstfiler, ellers kommer du til å ha mye søppel lenge i terminalvinduet ditt.

var async = krever ('async'), fs = krever ('fs'); async.waterfall ([funksjon (tilbakeringing) fs.readdir ('.', tilbakeringing); // les gjeldende katalog, send det videre til neste funksjon., funksjon (filnavn, tilbakeringing) // 'fileNames' er katalogoppføringen fra forrige funksjon async.map (filnavn, // Katalogoppføringen er bare en rekke filnavn, fs.stat, // slik at vi kan bruke async.map til å kjøre fs.stat for hver filnavnfunksjon (feil , statistikk) if (err) callback (err); annet tilbakeringing (feil, filnavn, statistikk); // passere feilen, katalogoppføringen og statsamlingen til neste punkt i fossen) ;, funksjon (filnavn, statistikk, tilbakeringing) // katalogoppføringen, 'filnavn' er samlet av samlingen av fs.stat objekter i 'statistikk' async.map (filnavn, funksjon (aFileName, readCallback) // Denne gangen tar vi filnavnene med kart og sender dem videre til fs.readFile for å få innholdet fs.readFile (aFileName, 'utf8', readCallback);, funksjon (feil, innhold) if (err) callback (err); else // Nå vår tilbakeringing w Ill har tre argumenter, den opprinnelige kataloglisten ('filnavn'), fs.stats-samlingen og en rekke med innholdet i hver fil tilbakeringing (feil, filnavn, statistikk, innhold); ); ], funksjon (feil, filnavn, statistikk, innhold) if (err) console.error (err);  annet console.log (filnavn); console.log (stats); console.log (innholdet); );

La oss si at vi vil få resultatene av bare filene som har en størrelse over 500 byte. Vi kan bruke koden ovenfor, men du vil få størrelsen og innholdet til hver fil, enten du trenger dem eller ikke. Hvordan kan du bare få statene til filene og bare innholdet i filene som oppfyller kravene til størrelse? 

For det første kan vi trekke alle de anonyme funksjonene ut i navngitte funksjoner. Det er personlig preferanse, men det gjør koden litt renere og lettere å forstå (gjenbrukbar for oppstart). Som du kanskje tror, ​​må du få størrelsene, vurdere størrelsene, og bare få innholdet i filene over størrelseskravet. Dette kan lett oppnås med noe som Array.filter, men det er en synkron funksjon, og async.waterfall forventer asynkron-stil funksjoner. Async.js har en hjelperfunksjon som kan bryte synkronfunksjoner inn i asynkrone funksjoner, det er ganske jazzily navngitt async.asyncify.

Vi må gjøre tre ting, som vi vil pakke sammen med async.asyncify. Først tar vi filnavnet og statestallene fra arrayFsStat funksjon, og vi vil fusjonere dem ved hjelp av kart. Da filtrerer vi ut noen elementer som har en statstørrelse mindre enn 300. Til slutt tar vi det kombinerte filnavnet og statobjektet og bruker kart igjen for å bare få filnavnet ut. 

Etter at vi har navnene på filene med en størrelse mindre enn 300, bruker vi async.map og fs.readFile for å få innholdet. Det er mange måter å knekke dette egget på, men i vårt tilfelle ble det brutt opp for å vise maksimal fleksibilitet og kodeutnyttelse. Dette async.waterfall Bruk illustrerer hvordan du kan mikse og matche synkron og asynkron kode.

var async = krever ('async'), fs = krever ('fs'); // Vår anonyme refactored til navngitte funksjoner funksjon directoryListing (tilbakeringing) fs.readdir ('.', Tilbakeringing);  funksjon arrayFsStat (filnavn, tilbakeringing) async.map (filnavn, fs.stat, funksjon (feil, statistikk) hvis (err) callback (err); annet tilbakeringing (feil, filnavn, statistikk); );  funksjon arrayFsReadFile (filnavn, tilbakeringing) async.map (filnavn, funksjon (aFileName, readCallback) fs.readFile (aFileName, 'utf8', readCallback);, funksjon (feil, innhold) if (err) callback (feil); else tilbakeringing (feil, innhold););  // Disse funksjonene er synkron funksjon mergeFilenameAndStat (filnavn, statistikk) return stats.map (funksjon (aStatObj, indeks) aStatObj.fileName = fileNames [index]; return aStatObj;);  funksjon over300 (combinedFilenamesAndStats) return combinedFilenamesAndStats .filter (funksjon (aStatObj) return aStatObj.size> = 300;);  fungere justFilenames (combinedFilenamesAndStats) return combinedFilenamesAndStats .map (funksjon (aCombinedFileNameAndStatObj) return aCombinedFileNameAndStatObj.fileName;);  async.waterfall ([DirectoryListing, arrayFsStat, async.asyncify (mergeFilenameAndStat), // asyncify wraps synkronfunksjoner i en feil-først tilbakeringing async.asyncify (above300), async.asyncify (justFilenames), arrayFsReadFile], funksjon (feil, innhold) hvis (err) console.error (err); else console.log (innhold););

Ta dette skrittet videre, la oss forfine vår funksjon enda lenger. La oss si at vi vil skrive en funksjon som fungerer akkurat som ovenfor, men med fleksibiliteten til å se på hvilken som helst bane. En nær fetter til async.waterfall er async.seq. Samtidig som async.waterfall utfører bare en foss av funksjoner, async.seq returnerer en funksjon som utfører en foss av andre funksjoner. I tillegg til å skape en funksjon, kan du sende inn verdier som går inn i den første asynkrone funksjonen. 

Konvertering til async.seq tar bare noen få modifikasjoner. Først vil vi endre directoryListing å akseptere et argument - dette vil være banen. For det andre legger vi til en variabel for å holde vår nye funksjon (directoryAbove300). Tredje, vi tar array argumentet fra async.waterfall og oversette det til argumenter for async.seq. Vårt tilbakekalling for fossen blir nå brukt som tilbakekalling når vi løper directoryAbove300.

var async = krever ('async'), fs = krever ('fs'), katalog over300; funksjon directoryListing (initialPath, tilbakeringing) // vi kan sende en variabel til den første funksjonen som brukes i async.seq - den resulterende funksjonen kan akseptere argumenter og sende dem denne første funksjonen fs.readdir (initialPath, tilbakeringing);  funksjon arrayFsStat (filnavn, tilbakeringing) async.map (filnavn, fs.stat, funksjon (feil, statistikk) hvis (err) callback (err); annet tilbakeringing (feil, filnavn, statistikk); );  funksjon arrayFsReadFile (filnavn, tilbakeringing) async.map (filnavn, funksjon (aFileName, readCallback) fs.readFile (aFileName, 'utf8', readCallback);, funksjon (feil, innhold) if (err) callback (feil); else tilbakeringing (feil, innhold););  funksjon mergeFilenameAndStat (filnavn, statistikk) return stats.map (funksjon (aStatObj, indeks) aStatObj.fileName = fileNames [index]; return aStatObj;);  funksjon over300 (combinedFilenamesAndStats) return combinedFilenamesAndStats .filter (funksjon (aStatObj) return aStatObj.size> = 300;);  fungere justFilenames (combinedFilenamesAndStats) return combinedFilenamesAndStats .map (funksjon (aCombinedFileNameAndStatObj) return aCombinedFileNameAndStatObj.fileName; //async.seq vil produsere en ny funksjon som du kan bruke igjen og igjen directoryAbove300 = async.seq (directoryListing, arrayFsStat, async.asyncify (mergeFilenameAndStat), async.asyncify (above300), async.asyncify (justFilenames), arrayFsReadFile); katalogAbove300 ('.', funksjon (feil, filnavn, statistikk, innhold) hvis (err) console.error (err); else console.log (filnavn););

En kommentar til løfter og asynkfunksjoner

Du lurer kanskje på hvorfor jeg ikke har nevnt løfter. Jeg har ingenting mot dem - de er ganske praktiske og kanskje en mer elegant løsning enn tilbakeringinger - men de er en annen måte å se på asynkron koding. 

Innebygde Node.js moduler bruker err-første tilbakeringinger, og tusenvis av andre moduler bruker dette mønsteret. Faktisk, det er derfor denne opplæringen bruker fs i eksemplene - noe som er så grunnleggende som filsystemtilgangen i Node.js bruker tilbakeringinger, er det så viktig at tilbakekallingskoder uten løfter er en viktig del av Node.js programmering.  

Det er mulig å bruke noe som Bluebird til å bryte opp err-første tilbakeringinger til Promise-baserte funksjoner, men det får bare deg så langt. Async.js gir en rekke metaforer som gjør asynkron kode lesbar og håndterbar.

Embrace Asynchrony

JavaScript har blitt et av de de facto språkene som fungerer på nettet. Det er ikke uten sine lærekurver, og det er nok av rammer og biblioteker for å holde deg opptatt også. Hvis du leter etter flere ressurser for å studere eller bruke i arbeidet ditt, sjekk ut hva vi har tilgjengelig på Envato-markedet.

Men å lære asynkron er noe helt annet, og forhåpentligvis har denne opplæringen vist deg hvor nyttig det kan være.

Asynkroni er nøkkelen til å skrive server-side JavaScript, men hvis den ikke er utformet på riktig måte, kan koden bli et uhåndterbart dyr av tilbakeringinger. Ved å bruke et bibliotek som async.js som gir en rekke metaforer, kan det hende du finner ut at den skriftlige asynkronkoden er en glede.