JavaScript tilbakeringinger, løfter og asynkfunksjoner Del 1

Introduksjon

Det er mye snakk om asynkron programmering, men hva er den store avtale? Den store avtalen er at vi vil at vår kode skal være ikke-blokkering. 

Oppgaver som kan blokkere applikasjonen vår inkluderer å lage HTTP-forespørsler, spørre en database eller åpne en fil. Noen språk, som Java, håndterer dette ved å opprette flere tråder. Imidlertid har JavaScript bare en tråd, så vi må designe våre programmer slik at ingen oppgave blokkerer strømmen. 

Asynkron programmering løser dette problemet. Det lar oss utføre oppgaver senere, slik at vi ikke holder opp hele programmet og venter på at oppgavene skal fullføres. Det hjelper også når vi vil sikre at oppgavene utføres i rekkefølge. 

I en del av denne opplæringen vil vi lære konseptene bak synkron og asynkron kode og se hvordan vi kan bruke tilbakeringingsfunksjoner for å løse problemer med asynkroni.

innhold

  • tråder
  • Synkron vs Asynkron
  • Tilbakeringingsfunksjoner
  • Sammendrag
  • ressurser

tråder

Jeg vil at du skal huske sist gang du handlet på matbutikken. Det var sannsynligvis flere kasseapparater åpne for å sjekke ut kunder. Dette hjelper butikkprosessen flere transaksjoner i samme tid. Dette er et eksempel på samtidighet. 

For å si det enkelt gjør samtidighet mer enn en oppgave samtidig. Operativsystemet ditt er samtidig fordi det kjører flere prosesser samtidig. En prosess er et eksekveringsmiljø eller en forekomst av et kjørende program. For eksempel er nettleseren, tekstredigeringsprogrammet og antivirusprogrammet alle prosessene på datamaskinen din som kjører samtidig.

Søknader kan også være samtidige. Dette oppnås med tråder. Jeg vil ikke bli for dyp fordi dette går ut over omfanget av denne artikkelen. Hvis du vil ha en grundig forklaring på hvordan JavaScript fungerer under hetten, anbefaler jeg at du ser på denne videoen. 

En tråd er en enhet i en prosess som kjører kode. I vårt butikkeksempel vil hver kassa være en tråd. Hvis vi bare har én kassa i butikken, ville det endre hvordan vi behandlet kunder. 

Har du noen gang vært i kø og hatt noe å holde opp transaksjonen din? Kanskje du trengte en priskontroll, eller måtte se en leder. Når jeg er på postkontoret prøver å sende en pakke, og jeg ikke har mine etiketter fylt ut, spør kassereren meg om å gå til side mens de fortsetter å sjekke ut andre kunder. Når jeg er klar, går jeg tilbake til forsiden av linjen for å bli sjekket ut. 

Dette ligner hvordan asynkron programmering fungerer. Kassereren kunne ha ventet på meg. Noen ganger gjør de det. Men det er en bedre opplevelse å ikke holde linjen opp og sjekke ut de andre kundene. Poenget er at kunder ikke trenger å bli sjekket ut i den rekkefølgen de er i kø. På samme måte må kode ikke utføres i den rekkefølge vi skriver det. 

Synkron vs Asynkron

Det er naturlig å tenke på vår kode som utføres sekvensielt fra topp til bunn. Dette er synkront. Men med JavaScript er enkelte oppgaver asynkron (for eksempel setTimeout), og noen oppgaver vi designer er asynkronte fordi vi vet at de kan blokkere forut for tiden. 

La oss ta en titt på et praktisk eksempel ved å bruke filer i Node.js. Hvis du vil prøve kodeeksemplene og trenger en primer på bruk av Node.js, kan du finne instruksjoner fra denne opplæringen. I dette eksemplet vil vi åpne en fil med innlegg og hente ett av innleggene. Da åpner vi en fil med kommentarer og henter kommentarene for det innlegget. 

Dette er den synkrone måten:

index.js

const fs = krever ('fs'); const path = krever ('path'); const postsUrl = path.join (__ dirname, 'db / posts.json'); const commentsUrl = path.join (__ dirname, 'db / comments.json'); // returnere dataene fra vår filfunksjon loadCollection (url) prøv const response = fs.readFileSync (url, 'utf8'); returnere JSON.parse (respons);  fangst (feil) console.log (feil);  // returnere et objekt ved id-funksjonen getRecord (samling, id) return collection.find (funksjon (element) return element.id == id;);  // returnere en rekke kommentarer til en innleggsfunksjon getCommentsByPost (kommentarer, postId) return comments.filter (funksjon (kommentar) return comment.postId == postId;);  // initialiseringskode const posts = loadCollection (postsUrl); const post = getRecord (innlegg, "001"); const kommentarer = loadCollection (commentsUrl); const postComments = getCommentsByPost (kommentarer, post.id); console.log (post); console.log (postComments);

db / posts.json

["id": "001", "tittel": "Hilsen", "tekst": "Hei Verden", "Forfatter": "Jane Doe", "id": "002", "tittel" "JavaScript 101", "Text": "Grunnleggende programmering.", "Forfatter": "Alberta Williams", "id": "003", "tittel": "Async Programming" Tilbakekall, løfter og async / avvente. "," Forfatter ":" Alberta Williams "] 

db / comments.json

["id": "phx732", "postId": "003", "tekst": "Jeg får ikke denne tilbakeringingsspillene." , "id": "avj9438", "postId": "003", "tekst": "Dette er virkelig nyttig info." , "id": "gnk368", "postId": "001", "tekst": "Dette er en testkommentar." ]

De readFileSync Metode åpner filen synkront. Derfor kan vi også skrive vår initialiseringskode på en synkron måte. Men dette er ikke den beste måten å åpne filen fordi dette er en potensielt blokkerende oppgave. Å åpne filen skal gjøres asynkront slik at gjennomføringsstrømmen kan være kontinuerlig. 

Node har a Readfile Metode vi kan bruke til å åpne filen asynkront. Dette er syntaksen:

fs.readFile (url, 'utf8', funksjon (feil, data) ...); 

Vi kan være fristet til å returnere dataene våre i denne tilbakeringingsfunksjonen, men det vil ikke være tilgjengelig for oss å bruke inne i vår loadCollection funksjon. Vår initialiseringskode må også endres fordi vi ikke har de riktige verdiene til å tilordne seg våre variabler. 

For å illustrere problemet, la oss se på et enklere eksempel. Hva tror du at følgende kode vil skrive ut?

funksjon oppgave1 () setTimeout (funksjon () console.log ('first');, 0);  funksjonsoppgave2 () console.log ('second');  funksjonsoppgave3 () console.log ('third');  oppgave 1(); Task2 (); task3 ();

Dette eksemplet vil skrive ut "andre", "tredje" og deretter "første". Det spiller ingen rolle at setTimeout funksjonen har en 0 forsinkelse. Det er en asynkron oppgave i JavaScript, så det vil alltid bli utsatt for å utføres senere. De firstTask funksjonen kan representere enhver asynkron oppgave, for eksempel å åpne en fil eller spørre vår database. 

En løsning for å få våre oppgaver å utføre i den rekkefølgen vi ønsker, er å bruke tilbakeringingsfunksjoner.

Tilbakeringingsfunksjoner

For å bruke tilbakeringingsfunksjoner, sender du en funksjon som en parameter til en annen funksjon, og deretter ring funksjonen når oppgaven er ferdig. Hvis du trenger en primer på hvordan du bruker høyere rekkefølgefunksjoner, har reaktivex en interaktiv opplæring du kan prøve. 

Tilbakeringinger la oss tvinge oppgaver til å utføre sekvensielt. De hjelper oss også når vi har oppgaver som avhenger av resultatene fra en tidligere oppgave. Ved hjelp av tilbakeringinger kan vi fikse vårt siste eksempel slik at det vil skrive ut "første", "andre" og deretter "tredje".

funksjon først (cb) setTimeout (funksjon () return cb ('first');, 0);  funksjon andre (cb) return cb ('second');  funksjon tredje (cb) return cb ('third');  første (funksjon (resultat1) console.log (result1); andre (funksjon (result2) console.log (result2); tredje (funksjon (result3) console.log (result3););); );

I stedet for å skrive ut strengen inne i hver funksjon, returnerer vi verdien inne i en tilbakeringing. Når vår kode er utført, skriver vi ut verdien som ble sendt inn i tilbakeringingen vår. Dette er hva vår Readfile funksjon bruker. 

Når vi går tilbake til filene våre, kan vi forandre vår loadCollection fungere slik at den bruker tilbakeringinger til å lese filen på asynkron måte.

funksjon loadCollection (url, tilbakeringing) fs.readFile (url, 'utf8', funksjon (feil, data) hvis (feil) console.log (feil); else return callback (JSON.parse (data)) ;); 

Og dette er hva vår initialiseringskode vil se ut som å bruke tilbakeringinger:

loadCollection (postsUrl, funksjon (innlegg) loadCollection (commentsUrl, funksjon (kommentarer) getRecord (innlegg, "001", funksjon (post) const postComments = getCommentsByPost (kommentarer, post.id); console.log (post); console.log (postkommentarer););););

En ting å legge merke til i vår loadCollection funksjon er det i stedet for å bruke a prøve / fangst uttalelse for å håndtere feil bruker vi en hvis / annet uttalelse. Fangstblokken vil ikke kunne fange feil returnert fra Readfile Ring tilbake. 

Det er god praksis å ha feilbehandlere i vår kode for feil som er resultatet av utvendige påvirkninger i motsetning til programmeringsfeil. Dette inkluderer tilgang til filer, tilkobling til en database, eller å lage en HTTP-forespørsel. 

I det reviderte kodeeksemplet har jeg ikke inkludert feilhåndtering. Hvis det skulle oppstå en feil ved noen av trinnene, fortsatte programmet ikke. Det ville være fint å gi meningsfulle instruksjoner.

Et eksempel på når feilhåndtering er viktig er at hvis vi har en oppgave å logge inn på en bruker. Dette innebærer å skaffe brukernavn og passord fra et skjema, spørre vår database for å se om det er en gyldig kombinasjon, og omdirigere brukeren til oversikten dersom vi lykkes. Hvis brukernavnet og passordet var ugyldig, ville vår app stoppe å kjøre hvis vi ikke forteller det hva du skal gjøre. 

En bedre brukeropplevelse ville være å returnere en feilmelding til brukeren og la dem prøve å logge på nytt. I vårt fileksempel kunne vi sende et feilobjekt sammen i tilbakeringingsfunksjonen. Dette er tilfellet med Readfile funksjon. Da, når vi utfører koden, kan vi legge til en hvis / annet uttalelse for å håndtere det vellykkede resultatet og det avviste resultatet.

Oppgave

Bruk tilbakeringingsmetoden, skriv et program som åpner en fil med brukere, velg en bruker, og åpne deretter en fil med innlegg og skriv ut brukerens info og alle innleggene sine.

Sammendrag

Asynkron programmering er en metode som brukes i vår kode for å utsette hendelser for senere utførelse. Når du arbeider med en asynkron oppgave, tilbakekallinger er en løsning for å klare oppgavene våre slik at de utfører sekvensielt. 

Hvis vi har flere oppgaver som er avhengige av resultatet av tidligere oppgaver, er en løsning å bruke flere nestede tilbakeringinger. Dette kan imidlertid føre til et problem som kalles "tilbakekallingshelv." Løfter løser problemet med tilbakekallingshelvete, og asynkfunksjoner lar oss skrive vår kode på en synkron måte. I del 2 av denne opplæringen lærer vi hva de er og hvordan de skal brukes i vår kode.

ressurser

  • Philip Roberts: Hva Heck er Event Loop uansett?
  • Sammenligning i Java
  • Du vet ikke JavaScript: Asynk og ytelse
  • Node.js Event Loop
  • Blokkering mot ikke-blokkering