Eric Elliott

Følg

Jan 23, 2017 · 11 min lese

Photo av Kabun (CC BY-NC-SA-2.,0)

«Master JavaScript-Intervju» er en serie av innlegg er designet for å forberede kandidatene for vanlige spørsmål de er sannsynlig å møte når du søker om et middels til høyt nivå JavaScript posisjon. Dette er spørsmål jeg ofte bruker i ekte intervjuer.

Et løfte er et objekt som kan produsere en enkelt verdi en gang i fremtiden: enten et løst verdi, eller en grunn til at det ikke er løst (for eksempel, et nettverk feil oppstod)., Et løfte kan være i en av 3 mulige tilstander: oppfylt, avvist, eller i påvente. Lover brukere kan legge tilbakering å håndtere oppfylt verdi eller det grunn for avvisning.

Løfter er ivrig, noe som betyr at et løfte vil begynne å gjøre hva oppgaven du gi den så snart som løftet constructor er tatt i bruk. Hvis du trenger lat, sjekk ut observables eller oppgaver.

En Ufullstendig Historie av Løfter

Tidlig implementering av lover og futures (en lignende / beslektede idé) begynte å dukke opp i språk som MultiLisp og Samtidig Prolog så tidlig som i 1980., Bruken av ordet «love» ble skapt av Barbara Liskov og Liuba Shrira i 1988.

første gang jeg hørte om lover i JavaScript, Node var helt ny og samfunnet var å diskutere den beste måten å håndtere asynkron atferd. Samfunnet eksperimentert med løfter for en stund, men til slutt slo seg ned på Node-standard error-første tilbakering.

Rundt samme tid, Dojo lagt løfter via Utsatt API. Økende interesse og aktivitet førte til slutt til det nyopprettede Lover/spesifikasjon som er designet for å gjøre ulike lover mer interoperable.,

jQuery er asynkron atferd var refactored rundt løfter. jQuery er løftet støtte hadde bemerkelsesverdige likheter til Dojo er Utsatt, og det ble raskt den mest brukte løfte implementering i JavaScript på grunn av jQuery er enorm popularitet — for en tid. Men, det gjorde ikke støtter to-kanal (er oppfylt/avvist) kjeding atferd & unntak ledelsen at folk var telle på å bygge verktøy på toppen av lover.,

til tross for disse svakhetene, jQuery offisielt gjort JavaScript lover mainstream og bedre frittstående løfte biblioteker som Q, Når, og Bluebird ble svært populære. jQuery er gjennomføring på inkompabiliteter motivert noen viktige avklaringer i løftet spec, som ble omskrevet og skiftet navn som Lover/A+ spesifikasjon.,

ES6 brakte et Lover/A+ kompatibel Promise global, og noen svært viktige Api ble bygget på toppen av den nye standarden Lover støtte: spesielt WHATWG Hente spec og Asynkron Funksjoner standard (en scene 3 utkast på den tiden dette ble skrevet).

De løfter som er beskrevet her, er de som er kompatibel med de Løfter/A+ spesifikasjon, med fokus på ECMAScript standard Promise gjennomføring.

Hvordan Løfter Arbeid

Et løfte er et objekt som kan bli returnert synkront fra en asynkron funksjon., Det vil være i en av 3 mulige tilstander:

Et løfte er avgjort, hvis det ikke er ventende (det har blitt løst eller avvist). Noen mennesker bruker løst, og slo seg til å bety det samme: ikke venter.

Når avgjort, et løfte kan ikke være bosatt. Ringer resolve() eller reject() igjen vil ha noen effekt. Den immutability av en avgjort løftet er en viktig funksjon.

Native JavaScript lover ikke utsett løfte stater. I stedet, du er forventet å behandle løftet som en svart boks., Bare funksjonen ansvarlig for å opprette løftet, vil ha kunnskap om lover status, eller tilgang til å løse eller avvis.

Her er en funksjon som returnerer et løfte som vil løse etter en bestemt tid:

vent — løftet eksempel på CodePen

Vår wait(3000) samtale venter 3000ms (3 sekunder), og logg deretter 'Hello!'., Alle spec-kompatibel lover definere en .then() metode som du kan bruke til å passere behandlere som kan ta den løst eller avvist verdi.

ES6 løfte konstruktør tar en funksjon. At funksjonen tar to parametre, resolve(), og reject(). I eksempelet ovenfor, kan vi bare ved hjelp av resolve(), så jeg forlot reject() av parameteren listen. Så vi kaller setTimeout() for å opprette forsinkelse, og ring resolve() når det er ferdig.,

Du kan eventuelt resolve() eller reject() med verdier, som vil bli sendt til callback-funksjoner festet med .then().

Når jeg reject() med en verdi, for jeg har alltid passere en Error objekt. Generelt vil jeg to mulige oppløsning stater: normal glad banen, eller et unntak — noe som stopper den normale glad banen skjer. Passerer en Error objekt gjør at eksplisitt.,

Viktige Lover Regler

En standard for løfter som ble definert av Lover/A+ spesifikasjon samfunnet. Det er mange implementeringer som er i samsvar med standarden, inkludert JavaScript standard ECMAScript løfter.

Løfter følgende spec må følge et bestemt sett av regler:

  • Et løfte eller «thenable» er et objekt som leverer et standard-kompatibel .then() metode.
  • En ventende løfte kan overgangen til et oppfylt eller avslått tilstand.,
  • Et oppfylt eller avvist lover er avgjort, og må ikke overgang til en annen stat.
  • Når et løfte er avgjort, må det ha en verdi (som kan være undefined). Denne verdien må ikke endres.

Endre i denne sammenheng refererer til identitet (===) sammenligningen. Et objekt kan brukes som oppfylt verdi, og objektet egenskaper kan mutere.,

Alle lover må levere en .then() metode med følgende signatur:

promise.then(
onFulfilled?: Function,
onRejected?: Function
) => Promise

.then() metode må være i samsvar med disse reglene:

  • Begge onFulfilled() og onRejected() er valgfritt.
  • Hvis argumentene følger ikke fungerer, må de bli ignorert.
  • onFulfilled() vil bli kalt når løftet er oppfylt, med løfte verdi som første argument.,
  • onRejected() vil bli kalt når løftet er avvist, med den begrunnelsen for avslaget som første argument. Årsaken kan være en hvilken som helst gyldig JavaScript verdi, men fordi avslag er i hovedsak synonymt med unntak, jeg anbefaler at du bruker Feil objekter.
  • Hverken onFulfilled() eller onRejected() kan kalles mer enn én gang.
  • .then() kan kalles mange ganger på det samme løftet. Med andre ord, et løfte som kan brukes til å samle tilbakering.,
  • .then() må returnere et nytt løfte, promise2.
  • Hvis onFulfilled() eller onRejected() returnere en verdi x, og x er et løfte, promise2 vil låse inn med (antar samme tilstand og verdi som) x. Ellers, promise2 vil bli oppfylt med verdien av x.,
  • Hvis onFulfilled eller onRejected kaster et unntak e, promise2 må bli avvist med e som den grunn.
  • Hvis onFulfilled er ikke en funksjon og promise1 er oppfylt, promise2 må være oppfylt med samme verdi som promise1.,
  • Hvis onRejected er ikke en funksjon og promise1 avslag promise2 må bli avvist med den samme grunn som promise1.

Løfte Lenker

Fordi .then() alltid returnerer en nye lover, det er mulig å kjede løfter med presis kontroll over hvor og hvordan feil håndteres. Lover tillater deg å etterligne en normal synkron koden er try/catch adferd.,

Som synkron-koden, kjeding vil resultere i en rekkefølge som går i serienummeret., Med andre ord, kan du gjøre:

fetch(url)
.then(process)
.then(save)
.catch(handleErrors)
;

Her er et eksempel på en kompleks løfte kjede med flere avvisninger:

Løfte kjeding atferd eksempel på CodePen

Feil Håndtering

Merk at lover har både en suksess og en feil handler, og det er veldig vanlig å se kode som gjør dette:

save().then(
handleSuccess,
handleError
);

Men hva skjer hvis handleSuccess() kaster en feil?, Løftet kom tilbake fra .then() vil bli avvist, men det er ingenting der å fange avvisning — noe som betyr at en feil i din app blir svelget. Oops!

derfor, noen mennesker vurdere koden ovenfor for å være en anti-mønster, og anbefaler følgende, i stedet:

save()
.then(handleSuccess)
.catch(handleError)
;

forskjellen er subtil, men viktig. I det første eksempelet, en feil med opprinnelse i save() drift vil bli fanget, men en feil med opprinnelse i handleSuccess() funksjon vil være svelging.,

Without .catch(), an error in the success handler is uncaught.

In the second example, .catch() will handle rejections from either save(), or handleSuccess().

With .catch(), both error sources are handled., (diagram kildekode)

selvfølgelig, save() feil kan være et nettverk feil, mens handleSuccess() feil kan være fordi utvikleren glemte å håndtere en bestemt status code. Hva hvis du ønsker å behandle dem annerledes? Du kan melde deg til å håndtere dem begge:

save()
.then(
handleSuccess,
handleNetworkError
)
.catch(handleProgrammerError)
;

Uansett hva du foretrekker, anbefaler jeg slutter alle lover kjeder med en .catch(). Det er verdt å gjenta:

jeg anbefaler slutter alle lover kjeder med en .catch().,

Hvordan avslutter jeg et Løfte?

En av de første tingene nye lover brukere ofte lurer på er hvordan du avbryter et løfte. Her er en idé: det er Bare å avvise løftet med «Kansellert» som begrunnelse. Hvis du trenger å håndtere det på en annen måte enn en «vanlig» feil, gjør din forgrening i din feilbehandleren.

Her er noen av de vanligste feilene folk gjør når de ruller sine egne lover avbestilling:

Legge til .,avbryt() for å løfte

å Legge til .cancel() gjør det lover ikke-standard, men det krenker også en annen regel av lover: Bare funksjonen som skaper lover skal være i stand til å løse, avvise eller avslutte lover. Å utsette den bryter som innkapsling, og oppfordrer folk til å skrive kode som manipulerer løftet på steder som ikke bør vite om det. Unngå spaghetti og brutte løfter.

Glemme å rydde opp

Noen smarte folk har funnet ut at det er en måte å bruke Promise.race() som en avbestilling mekanisme., Problemet med det er at avbestilling kontroll er tatt fra den funksjon som skaper løfte, som er det eneste stedet du kan utføre skikkelig opprydding aktiviteter, slik som rydding tidsavbrudd eller frigjøre minne ved å fjerne referanser til data, etc…

Glemme å håndtere en avvist avbryt løfte

Visste du at Chrome kaster advarsel meldinger over hele konsollen når du glemmer å håndtere et løfte avvisning? Oops!

Altfor komplekse

Det trukket TC39 forslag for avbestilling foreslått en egen meldinger kanal for avbestilling., Det er også brukt et nytt konsept kalt en avbestilling token. I min mening, den løsning ville ha betydelig oppblåst løftet spec, og den eneste funksjonen ville det ha forutsatt at spekulasjoner ikke direkte støtte er separasjon av avslag og avbestillinger, som IMO, er ikke nødvendig å begynne med.

Vil du ønsker å gjøre bytte avhengig av om det er et unntak, eller en oppsigelse? Ja, så absolutt. Er at løftet jobb? I min mening, nei, det er det ikke.,

Dette er Løftet Avbestilling

Vanligvis, jeg passerer all informasjon løftet behov for å finne ut hvordan å løse / avslå / avslutte når som lover etableringen tid. På den måten, er det ikke behov for en .cancel() metode på et løfte. Du kanskje lurer på hvordan du kan vite om du kommer til å avbryte når som lover etableringen tid.

«Hvis jeg ennå ikke vet om eller ikke å kansellere, hvordan vil jeg vite hva du skal passere i når jeg lager den lover?,»

Hvis bare det var noen slags objekt som kunne stå i for en potensiell verdi i fremtiden… nei, vent.

Den verdien vi passere i å representere om ikke å avbryte kan være et løfte seg selv. Her er hvordan det kan se ut:

Kansellerbare vente — prøv det på CodePen

Vi bruker standard parameter oppdrag å fortelle det til ikke å avbryte standard. Det gjør det cancel parameter praktisk tilleggsutstyr., Da vi satt på venting slik vi gjorde før, men denne gangen kan vi ta timeout-ID, slik at vi kan fjerne det senere.

Vi bruke cancel.then() metode for å håndtere avbestilling og ressurs opprydding. Dette vil bare kjøre hvis løftet blir avlyst før den har en sjanse til å løse. Hvis du avbestiller for sent, du har gått glipp av din sjanse. Det toget har forlatt stasjonen.

Merk: Du lurer kanskje på hva noop() funksjonen er for. Ordet noop står for no-op, noe som betyr en funksjon som gjør noe., Uten det, V8 vil kaste advarsler: UnhandledPromiseRejectionWarning: Unhandled promise rejection. Det er en god idé å alltid løfte håndtaket avslag, selv om behandleren er en noop().

Abstracting Løfte Avbestilling

Dette er greit for en wait() tidtaker, men vi kan sammendrag denne ideen videre til å kapsle inn alt du har å huske på:

  1. Avvis avbryt løfte standard — vi ønsker ikke å avbryte eller kaste feil hvis det ikke avbryt lover blir vedtatt.
  2. Husk å utføre cleanup når du avviser for avbestilling.,
  3. Husk at onCancel opprydding kan selv kaste en feil, og at feil vil trenge behandling, også. (Merk at feil håndtering er utelatt i vente eksemplet ovenfor — er det lett å glemme!)

La oss lage en kansellerbare løfte verktøy som du kan bruke til å bryte noen lover., For eksempel, for å håndtere nettverket forespørsler, etc… signaturen vil se ut som dette:

speculation(fn: SpecFunction, shouldCancel: Promise) => Promise

SpecFunction er akkurat som den funksjonen du vil passere inn Promise konstruktør, med ett unntak — det tar en onCancel() handler:

SpecFunction(resolve: Function, reject: Function, onCancel: Function) => Void

Merk at i dette eksemplet er bare en illustrasjon for å gi deg den hovedpunkt av hvordan det fungerer. Det er noen andre kanten tilfeller må du ta i betraktning., For eksempel, i denne versjonen, handleCancel vil bli oppringt hvis du vil avbryte løftet etter det er allerede avgjort.

jeg har implementert en opprettholdes produksjonen versjon av denne med kant tilfeller dekket som åpen kildekode bibliotek, Spekulasjoner.

La oss bruke den forbedrede bibliotek abstraksjon for å omskrive kansellerbare wait() verktøy fra før., Først installere spekulasjoner:

npm install --save speculation

Nå kan du importere og bruke det:

Dette forenkler ting litt, fordi du ikke trenger å bekymre deg noop(), fanger feil i onCancel(), funksjon eller andre kanten tilfeller. Disse detaljene har blitt abstrahert bort av speculation(). Sjekk det ut og føl deg fri til å bruke den i reelle prosjekter.,

Tilbehør av de Innfødte JS Løfte

native Promise objektet har noen ekstra ting du kan være interessert i:

  • Promise.reject() returnerer en avvist lover.
  • Promise.resolve() returnerer et løst lover.
  • Promise.race() tar en matrise (eller noen iterable) og returnerer et løfte som løser med verdien av den første løst løfte i iterable, eller avviser med på grunn av det første løftet som avviser.,
  • Promise.all() tar en matrise (eller noen iterable) og returnerer et løfte som løser når alle løfter i iterable argumentet har løst, eller avviser med grunn av den første vedtatt lover som avviser.

Konklusjon

Lover har blitt en integrert del av flere uttrykk i JavaScript, inkludert WHATWG Hente standarden som brukes for de fleste moderne ajax forespørsler, og Asynkron Funksjoner standard som brukes til å lage asynkron kode ser synkron.,

Asynkron funksjoner er fase 3 på den tiden dette ble skrevet, men jeg spår at de vil snart bli en svært populær, svært vanlig løsning for asynkron programmering i JavaScript — noe som betyr at det å lære å sette pris på lover kommer til å bli enda mer viktig å JavaScript-utviklere i nær fremtid.

For eksempel, hvis du bruker Redux, foreslår jeg at du sjekker ut redux-saga: Et bibliotek som brukes til å behandle bivirkninger i Redux som avhenger av asynkron funksjoner i hele dokumentasjonen.,

jeg håper selv opplevd løfte brukere har en bedre forståelse av hvilke løfter er og hvordan de fungerer, og hvordan du bruker dem bedre etter å ha lest dette.

Utforsk den Serien

  • Hva er en Avslutning?
  • Hva er Forskjellen Mellom Klasse og Prototypal Arv?
  • Hva er en Ren Funksjon?
  • Hva er Funksjonen Komposisjon?
  • Hva er Funksjonell Programmering?
  • Hva er et Løfte?
  • Myke Ferdigheter

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *