JavaScript er et asynkront (ikke-blokkerende) og entrådet programmeringsspråk, noe som betyr at bare én prosess kan kjøres om gangen.
I programmeringsspråk refererer tilbakeringingshelvete generelt til en ineffektiv måte å skrive kode på med asynkrone anrop. Det er også kjent som Doom-pyramiden.
Tilbakeringingshelvetet i JavaScript omtales som en situasjon der en overdreven mengde nestede tilbakeringingsfunksjoner blir utført. Det reduserer kodelesbarhet og vedlikehold. Tilbakeringingshelvete-situasjonen oppstår vanligvis når man arbeider med asynkrone forespørselsoperasjoner, for eksempel å lage flere API-forespørsler eller håndtere hendelser med komplekse avhengigheter.
For bedre å forstå tilbakeringingshelvetet i JavaScript, må du først forstå tilbakeringingene og hendelsesløkkene i JavaScript.
Tilbakeringinger i JavaScript
JavaScript betrakter alt som et objekt, for eksempel strenger, matriser og funksjoner. Derfor lar tilbakeringingskonseptet oss overføre funksjonen som et argument til en annen funksjon. Tilbakeringingsfunksjonen vil fullføre utførelsen først, og den overordnede funksjonen vil bli utført senere.
Tilbakeringingsfunksjonene utføres asynkront og lar koden fortsette å kjøre uten å vente på å fullføre den asynkrone oppgaven. Når flere asynkrone oppgaver kombineres, og hver oppgave avhenger av dens forrige oppgave, blir kodestrukturen komplisert.
La oss forstå bruken og viktigheten av tilbakeringingene. La oss anta et eksempel vi har en funksjon som tar tre parametere en streng og to tall. Vi vil ha noe utdata basert på strengteksten med flere betingelser.
Tenk på eksemplet nedenfor:
function expectedResult(action, x, y){ if(action === 'add'){ return x+y }else if(action === 'subtract'){ return x-y } } console.log(expectedResult('add',20,10)) console.log(expectedResult('subtract',30,10))
Produksjon:
30 20
Koden ovenfor vil fungere fint, men vi må legge til flere oppgaver for å gjøre koden skalerbar. Antall betingede utsagn vil også fortsette å øke, noe som vil føre til en rotete kodestruktur som må optimaliseres og leses.
q2 måneder
Så vi kan skrive om koden på en bedre måte som følger:
function add(x,y){ return x+y } function subtract(x,y){ return x-y } function expectedResult(callBack, x, y){ return callBack(x,y) } console.log(expectedResult(add, 20, 10)) console.log(expectedResult(subtract, 30, 10))
Produksjon:
30 20
Likevel vil utgangen være den samme. Men i eksemplet ovenfor har vi definert dens separate funksjonskropp og sendt funksjonen som en tilbakeringingsfunksjon til forventetResult-funksjonen. Derfor, hvis vi ønsker å utvide funksjonaliteten til de forventede resultatene slik at vi kan opprette et annet fungerende organ med en annen operasjon og bruke den som tilbakeringingsfunksjon, vil det gjøre det lettere å forstå og forbedre kodens lesbarhet.
Det er andre forskjellige eksempler på tilbakeringing tilgjengelig i støttede JavaScript-funksjoner. Noen få vanlige eksempler er hendelseslyttere og array-funksjoner som kart, reduser, filter osv.
For bedre å forstå det, bør vi forstå JavaScripts pass-by-value og pass-by-referanse.
JavaScript støtter to typer datatyper som er primitive og ikke-primitive. Primitive datatyper er undefined, null, string og boolean, som ikke kan endres, eller vi kan si uforanderlige sammenlignende; ikke-primitive datatyper er matriser, funksjoner og objekter som kan endres eller endres.
Pass by reference sender referanseadressen til en enhet, som en funksjon kan tas som et argument. Så hvis verdien i den funksjonen endres, vil den endre den opprinnelige verdien, som er tilgjengelig utenfor funksjonen.
Til sammenligning endrer ikke pass-by-value-konseptet sin opprinnelige verdi, som er tilgjengelig utenfor funksjonskroppen. I stedet vil den kopiere verdien til to forskjellige steder ved å bruke minnet deres. JavaScript identifiserte alle objektene ved referanse.
I JavaScript lytter addEventListener etter hendelsene som klikk, mouseover og mouseout og tar det andre argumentet som en funksjon som vil bli utført når hendelsen utløses. Denne funksjonen brukes pass by reference-konsept og bestått uten parentes.
Tenk på eksemplet nedenfor; i dette eksemplet har vi sendt en hilsen-funksjon som et argument inn i addEventListener som tilbakeringingsfunksjon. Den vil bli påkalt når klikkhendelsen utløses:
Test.html:
Javascript Callback Example <h3>Javascript Callback</h3> Click Here to Console const button = document.getElementById('btn'); const greet=()=>{ console.log('Hello, How are you?') } button.addEventListener('click', greet)
Produksjon:
I eksemplet ovenfor har vi sendt en hilsen-funksjon som et argument inn i addEventListener som tilbakeringingsfunksjon. Den vil bli påkalt når klikkhendelsen utløses.
På samme måte er filteret også et eksempel på tilbakeringingsfunksjonen. Hvis vi bruker et filter for å iterere en matrise, vil det ta en annen tilbakeringingsfunksjon som et argument for å behandle matrisedataene. Tenk på eksempelet nedenfor; i dette eksemplet bruker vi større-funksjonen for å skrive ut tallet større enn 5 i matrisen. Vi bruker isGreater-funksjonen som en tilbakeringingsfunksjon i filtermetoden.
const arr = [3,10,6,7] const isGreater = num => num > 5 console.log(arr.filter(isGreater))
Produksjon:
[ 10, 6, 7 ]
Eksempelet ovenfor viser at den større funksjonen brukes som en tilbakeringingsfunksjon i filtermetoden.
For bedre å forstå tilbakeringingene, hendelsesløkkene i JavaScript, la oss diskutere synkron og asynkron JavaScript:
Synkron JavaScript
La oss forstå hva som er funksjonene til et synkront programmeringsspråk. Synkron programmering har følgende funksjoner:
Blokkering av utførelse: Det synkrone programmeringsspråket støtter blokkeringsteknikken, noe som betyr at den blokkerer kjøringen av påfølgende setninger som eksisterende setninger vil bli utført. Dermed oppnår den en forutsigbar og deterministisk kjøring av setningene.
Sekvensiell flyt: Synkron programmering støtter den sekvensielle utførelsesflyten, noe som betyr at hver setning utføres på en sekvensiell måte som den ene etter den andre. Språkprogrammet venter på at en uttalelse skal fullføres før den går videre til neste.
Enkelhet: Synkron programmering anses ofte som lett å forstå fordi vi kan forutsi rekkefølgen av utførelsesflyten. Generelt er det lineært og lett å forutsi. De små applikasjonene er gode å utvikle på disse språkene fordi de kan håndtere den kritiske rekkefølgen av operasjoner.
Direkte feilhåndtering: I et synkront programmeringsspråk er feilhåndtering veldig enkelt. Hvis det oppstår en feil når en setning kjøres, vil den gi en feil og programmet kan fange den.
I et nøtteskall har synkron programmering to kjernefunksjoner, det vil si at en enkelt oppgave utføres om gangen, og det neste settet med følgende oppgaver vil bare bli adressert når den gjeldende oppgaven er fullført. Derved følger den en sekvensiell kodekjøring.
det vakreste smilet
Denne oppførselen til programmeringen når en setning kjøres, skaper en situasjon med blokkkode ettersom hver jobb må vente på at den forrige jobben skal fullføres.
Men når folk snakker om JavaScript, har det alltid vært et gåtefullt svar om det er synkront eller asynkront.
I de ovenfor diskuterte eksemplene, når vi brukte en funksjon som tilbakeringing i filterfunksjonen, ble den utført synkront. Derfor kalles det en synkron utførelse. Filterfunksjonen må vente til den større funksjonen fullfører utførelsen.
Derfor kalles tilbakeringingsfunksjonen også blokkering av tilbakeringinger, ettersom den blokkerer utførelsen av overordnet funksjon der den ble påkalt.
Primært betraktes JavaScript som en-tråds synkron og blokkerende i naturen. Men ved å bruke noen få tilnærminger kan vi få det til å fungere asynkront basert på forskjellige scenarier.
La oss nå forstå asynkron JavaScript.
Asynkron JavaScript
Det asynkrone programmeringsspråket fokuserer på å forbedre applikasjonens ytelse. Tilbakeringingene kan brukes i slike scenarier. Vi kan analysere den asynkrone oppførselen til JavaScript ved hjelp av eksemplet nedenfor:
function greet(){ console.log('greet after 1 second') } setTimeout(greet, 1000)
Fra eksemplet ovenfor tar funksjonen setTimeout inn en tilbakeringing og tid i millisekunder som argumenter. Tilbakeringingen blir påkalt etter den nevnte tiden (her 1s). I et nøtteskall vil funksjonen vente i 1s for utførelse. Ta en titt på koden nedenfor:
c struktur i struktur
function greet(){ console.log('greet after 1 second') } setTimeout(greet, 1000) console.log('first') console.log('Second')
Produksjon:
first Second greet after 1 second
Fra koden ovenfor vil loggmeldingene etter setTimeout bli utført først mens tidtakeren vil passere. Derfor resulterer det i ett sekund og deretter en hilsen etter 1 sekunds tidsintervall.
I JavaScript er setTimeout en asynkron funksjon. Hver gang vi kaller funksjonen setTimeout, registrerer den en tilbakeringingsfunksjon (greet i dette tilfellet) som skal utføres etter den angitte forsinkelsen. Den blokkerer imidlertid ikke kjøringen av den påfølgende koden.
I eksemplet ovenfor er loggmeldingene de synkrone setningene som kjøres umiddelbart. De er ikke avhengige av funksjonen setTimeout. Derfor utfører og logger de sine respektive meldinger til konsollen uten å vente på forsinkelsen spesifisert i setTimeout.
I mellomtiden håndterer hendelsesløkken i JavaScript de asynkrone oppgavene. I dette tilfellet venter den på at den spesifiserte forsinkelsen (1 sekund) skal passere, og etter at tiden har gått, henter den tilbakeringingsfunksjonen (greet) og utfører den.
Dermed ble den andre koden etter setTimeout-funksjonen kjørt mens den kjørte i bakgrunnen. Denne virkemåten lar JavaScript utføre andre oppgaver mens du venter på at den asynkrone operasjonen skal fullføres.
Vi må forstå anropsstakken og tilbakeringingskøen for å håndtere de asynkrone hendelsene i JavaScript.
Tenk på bildet nedenfor:
Fra bildet ovenfor består en typisk JavaScript-motor av et heap-minne og en anropsstabel. Anropsstakken utfører all koden uten å vente når den skyves til stabelen.
Heap-minnet er ansvarlig for å tildele minnet for objekter og funksjoner under kjøring når de er nødvendig.
Nå består nettlesermotorene våre av flere nett-APIer som DOM, setTimeout, konsoll, henting osv., og motoren kan få tilgang til disse APIene ved å bruke det globale vindusobjektet. I neste trinn spiller noen hendelsesløkker rollen som gatekeeper som plukker funksjonsforespørsler inne i tilbakeringingskøen og skyver dem inn i stabelen. Disse funksjonene, som setTimeout, krever en viss ventetid.
La oss nå gå tilbake til vårt eksempel, funksjonen setTimeout; når funksjonen oppdages, blir timeren registrert i tilbakeringingskøen. Etter dette blir resten av koden presset inn i anropsstakken og blir utført når funksjonen når sin timergrense, den er utløpt, og tilbakeringingskøen skyver tilbakeringingsfunksjonen, som har den spesifiserte logikken og er registrert i tidsavbruddsfunksjonen . Dermed vil den bli utført etter den angitte tiden.
Callback Hell Scenarios
Nå har vi diskutert tilbakeringinger, synkron, asynkron og andre relevante emner for tilbakeringingshelvetet. La oss forstå hva tilbakeringingshelvete er i JavaScript.
Situasjonen når flere tilbakeringinger er nestet er kjent som tilbakeringingshelvetet siden dens kodeform ser ut som en pyramide, som også kalles 'undergangspyramiden'.
Tilbakeringingshelvetet gjør det vanskeligere å forstå og vedlikeholde koden. Vi kan stort sett se denne situasjonen mens vi jobber i node JS. Tenk for eksempel på eksemplet nedenfor:
getArticlesData(20, (articles) => { console.log('article lists', articles); getUserData(article.username, (name) => { console.log(name); getAddress(name, (item) => { console.log(item); //This goes on and on... } })
I eksemplet ovenfor tar getUserData et brukernavn som er avhengig av artikkellisten eller som må trekkes ut getArticles-svar som er inne i artikkelen. getAddress har også en lignende avhengighet, som er avhengig av getUserDatas svar. Denne situasjonen kalles tilbakeringingshelvete.
Den interne funksjonen til tilbakeringingshelvetet kan forstås med eksemplet nedenfor:
La oss forstå at vi må utføre oppgave A. For å utføre en oppgave trenger vi noen data fra oppgave B. Tilsvarende; vi har forskjellige oppgaver som er avhengige av hverandre og utføres asynkront. Dermed skaper den en rekke tilbakeringingsfunksjoner.
La oss forstå løftene i JavaScript og hvordan de lager asynkrone operasjoner, slik at vi kan unngå å skrive nestede tilbakeringinger.
JavaScript lover
I JavaScript ble løfter introdusert i ES6. Det er en gjenstand med et syntaktisk belegg. På grunn av dens asynkrone oppførsel er det en alternativ måte å unngå å skrive tilbakeringinger for asynkrone operasjoner. I dag implementeres web-API-er som fetch() ved å bruke det lovende, som gir en effektiv måte å få tilgang til dataene fra serveren på. Det forbedret også kodelesbarheten og er en måte å unngå å skrive nestede tilbakeringinger.
Løfter i det virkelige liv uttrykker tillit mellom to eller flere personer og en forsikring om at en bestemt ting sikkert vil skje. I JavaScript er et løfte et objekt som sikrer å produsere en enkelt verdi i fremtiden (når nødvendig). Promise i JavaScript brukes til å administrere og takle asynkrone operasjoner.
Løftet returnerer et objekt som sikrer og representerer fullføringen eller feilen av asynkrone operasjoner og dens utgang. Det er en proxy for en verdi uten å vite den nøyaktige utgangen. Det er nyttig for asynkrone handlinger for å gi en eventuell suksessverdi eller feilårsak. Dermed returnerer de asynkrone metodene verdiene som en synkron metode.
Generelt har løftene følgende tre tilstander:
- Oppfylt: Oppfylt tilstand er når en utført handling er løst eller fullført.
- Venter: Venter-tilstanden er når forespørselen er under behandling, og den iverksatte handlingen er verken løst eller avvist og er fortsatt i sin opprinnelige tilstand.
- Avvist: Den avviste tilstanden er når den påførte handlingen har blitt avvist, noe som fører til at den ønskede operasjonen mislykkes. Årsaken til avvisningen kan være hva som helst, inkludert at serveren er nede.
Syntaksen for løftene:
let newPromise = new Promise(function(resolve, reject) { // asynchronous call is made //Resolve or reject the data });
Nedenfor er et eksempel på å skrive løftene:
Dette er et eksempel på å skrive et løfte.
function getArticleData(id) { return new Promise((resolve, reject) => { setTimeout(() => { console.log('Fetching data....'); resolve({ id: id, name: 'derik' }); }, 5000); }); } getArticleData('10').then(res=> console.log(res))
I eksemplet ovenfor kan vi se hvordan vi effektivt kan bruke løftene til å sende en forespørsel fra serveren. Vi kan observere at kodelesbarheten økes i koden ovenfor enn i tilbakeringingene. Løfter gir metoder som .then() og .catch(), som lar oss håndtere operasjonsstatus i tilfelle suksess eller fiasko. Vi kan spesifisere tilfellene for de forskjellige tilstandene til løftene.
Async/Await i JavaScript
Det er en annen måte å unngå bruk av nestede tilbakeringinger. Async/ Await lar oss bruke løftene mye mer effektivt. Vi kan unngå å bruke .then() eller .catch() metodekjeding. Disse metodene er også avhengige av tilbakeringingsfunksjonene.
Async/Await kan brukes nøyaktig med Promise for å forbedre applikasjonens ytelse. Det løste løftene internt og ga resultatet. Også, igjen, er det mer lesbart enn () eller catch()-metodene.
Vi kan ikke bruke Async/Await med de vanlige tilbakeringingsfunksjonene. For å bruke det må vi gjøre en funksjon asynkron ved å skrive et asynkront nøkkelord før funksjonsnøkkelordet. Men internt bruker den også kjetting.
Nedenfor er et eksempel på Async/Await:
async function displayData() { try { const articleData = await getArticle(10); const placeData = await getPlaces(article.name); const cityData = await getCity(place) console.log(city); } catch (err) { console.log('Error: ', err.message); } } displayData();
For å bruke Async/Await må funksjonen spesifiseres med async nøkkelordet, og await nøkkelordet skal skrives inne i funksjonen. Asynkroniseringen vil stoppe utførelsen til løftet er løst eller avvist. Det vil bli gjenopptatt når løftet er utdelt. Når det er løst, vil verdien av await-uttrykket lagres i variabelen som inneholder det.
Sammendrag:
I et nøtteskall kan vi unngå nestede tilbakeringinger ved å bruke løftene og asynkronisere/avvente. Bortsett fra disse kan vi følge andre tilnærminger, for eksempel å skrive kommentarer, og å dele koden i separate komponenter kan også være nyttig. Men i dag foretrekker utviklerne bruken av async/wait.
Konklusjon:
Tilbakeringingshelvetet i JavaScript omtales som en situasjon der en overdreven mengde nestede tilbakeringingsfunksjoner blir utført. Det reduserer kodelesbarhet og vedlikehold. Tilbakeringingshelvete-situasjonen oppstår vanligvis når man arbeider med asynkrone forespørselsoperasjoner, for eksempel å lage flere API-forespørsler eller håndtere hendelser med komplekse avhengigheter.
java boolsk streng
For bedre å forstå tilbakeringingshelvetet i JavaScript.
JavaScript betrakter alt som et objekt, for eksempel strenger, matriser og funksjoner. Derfor lar tilbakeringingskonseptet oss overføre funksjonen som et argument til en annen funksjon. Tilbakeringingsfunksjonen vil fullføre utførelsen først, og den overordnede funksjonen vil bli utført senere.
Tilbakeringingsfunksjonene utføres asynkront og lar koden fortsette å kjøre uten å vente på å fullføre den asynkrone oppgaven.