JavaScript je asinhroni (neblokirni) programski jezik z eno nitjo, kar pomeni, da se lahko naenkrat izvaja samo en proces.
V programskih jezikih se povratni klic na splošno nanaša na neučinkovit način pisanja kode z asinhronimi klici. Znana je tudi kot Piramida pogube.
Pekel povratnega klica v JavaScriptu se imenuje situacija, ko se izvaja prevelika količina ugnezdenih funkcij povratnega klica. Zmanjša berljivost kode in vzdrževanje. Situacija povratnega klica se običajno pojavi, ko se ukvarjate z asinhronimi operacijami zahtev, kot je izdelava več zahtev API ali obravnava dogodkov s kompleksnimi odvisnostmi.
Če želite bolje razumeti povratni klic v JavaScriptu, najprej razumejte povratne klice in zanke dogodkov v JavaScriptu.
Povratni klici v JavaScriptu
JavaScript vse obravnava kot objekt, kot so nizi, nizi in funkcije. Zato nam koncept povratnega klica omogoča, da funkcijo posredujemo kot argument drugi funkciji. Funkcija povratnega klica bo najprej dokončala izvedbo, nadrejena funkcija pa se bo izvedla pozneje.
Funkcije povratnega klica se izvajajo asinhrono in omogočajo, da se koda nadaljuje, ne da bi čakala na dokončanje asinhrone naloge. Ko je združenih več asinhronih nalog in je vsaka naloga odvisna od svoje prejšnje naloge, postane struktura kode zapletena.
Razumejmo uporabo in pomen povratnih klicev. Recimo primer, da imamo funkcijo, ki sprejme tri parametre en niz in dve števili. Želimo nekaj izhoda na podlagi besedila niza z več pogoji.
Razmislite o spodnjem primeru:
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))
Izhod:
30 20
Zgornja koda bo delovala v redu, vendar moramo dodati več nalog, da bo koda razširljiva. Še naprej se bo povečevalo tudi število pogojnih stavkov, kar bo privedlo do neurejene strukture kode, ki jo je treba optimizirati in berljivo.
Torej lahko kodo prepišemo na boljši način, kot sledi:
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))
Izhod:
30 20
Kljub temu bo rezultat enak. Toda v zgornjem primeru smo definirali njegovo ločeno telo funkcije in funkcijo kot povratni klic posredovali funkcijipričakovanegaRezultata. Če torej želimo razširiti funkcionalnost pričakovanih rezultatov, tako da lahko ustvarimo drugo delujoče telo z drugačno operacijo in ga uporabimo kot funkcijo povratnega klica, bo to olajšalo razumevanje in izboljšalo berljivost kode.
V podprtih funkcijah JavaScript so na voljo drugi različni primeri povratnih klicev. Nekaj pogostih primerov so poslušalci dogodkov in matrične funkcije, kot so preslikava, zmanjšanje, filtriranje itd.
Da bi ga bolje razumeli, bi morali razumeti prehod po vrednosti in prenos po sklicu JavaScripta.
JavaScript podpira dve vrsti podatkovnih tipov, ki sta primitivni in neprimitivni. Primitivni podatkovni tipi so undefined, null, string in boolean, ki jih ni mogoče spremeniti ali primerjalno lahko rečemo nespremenljivi; neprimitivni podatkovni tipi so nizi, funkcije in objekti, ki jih je mogoče spreminjati ali spreminjati.
Prenos po sklicu posreduje referenčni naslov entitete, tako kot se lahko funkcija vzame kot argument. Torej, če je vrednost znotraj te funkcije spremenjena, bo to spremenilo prvotno vrednost, ki je na voljo zunaj funkcije.
Koncept prenosa po vrednosti primerjalno ne spremeni svoje prvotne vrednosti, ki je na voljo zunaj telesa funkcije. Namesto tega bo kopiral vrednost na dve različni lokaciji z uporabo njunega pomnilnika. JavaScript je identificiral vse predmete glede na njihovo referenco.
V JavaScriptu addEventListener posluša dogodke, kot so klik, prehod miške in izhod miške, ter vzame drugi argument kot funkcijo, ki se bo izvršila, ko se dogodek sproži. Ta funkcija se uporablja za koncept prenosa sklicevanja in se prenaša z uporabo brez oklepajev.
kako pretvoriti niz v int
Razmislite o spodnjem primeru; v tem primeru smo posredovali funkcijo greet kot argument v addEventListener kot funkcijo povratnega klica. Priklican bo, ko se sproži dogodek klika:
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)
Izhod:
V zgornjem primeru smo posredovali funkcijo pozdrav kot argument v addEventListener kot funkcijo povratnega klica. Priklican bo, ko se sproži dogodek klika.
Podobno je filter tudi primer funkcije povratnega klica. Če za ponavljanje matrike uporabimo filter, bo kot argument za obdelavo podatkov matrike vzel drugo funkcijo povratnega klica. Razmislite o spodnjem primeru; v tem primeru uporabljamo funkcijo larger za tiskanje števila, večjega od 5 v matriki. Funkcijo isGreater uporabljamo kot funkcijo povratnega klica v metodi filtra.
const arr = [3,10,6,7] const isGreater = num => num > 5 console.log(arr.filter(isGreater))
Izhod:
[ 10, 6, 7 ]
Zgornji primer kaže, da se funkcija larger uporablja kot funkcija povratnega klica v metodi filtra.
Da bi bolje razumeli povratne klice in zanke dogodkov v JavaScriptu, razpravljajmo o sinhronem in asinhronem JavaScriptu:
Sinhroni JavaScript
Razumejmo, kakšne so značilnosti sinhronega programskega jezika. Sinhrono programiranje ima naslednje lastnosti:
Blokiranje izvedbe: Sinhroni programski jezik podpira tehniko blokiranja izvajanja, kar pomeni, da blokira izvajanje naslednjih stavkov, ki se bodo izvajali obstoječi stavki. Tako doseže predvidljivo in deterministično izvajanje stavkov.
Zaporedni tok: Sinhrono programiranje podpira zaporedni tok izvajanja, kar pomeni, da se vsak stavek izvaja zaporedno, kot eden za drugim. Jezikovni program počaka, da se stavek zaključi, preden preide na naslednjega.
Enostavnost: Sinhrono programiranje pogosto velja za enostavno razumljivo, ker lahko predvidimo njegov vrstni red izvedbenega toka. Na splošno je linearen in ga je enostavno predvideti. Majhne aplikacije je dobro razviti v teh jezikih, ker lahko obvladajo kritičen vrstni red operacij.
Neposredna obravnava napak: V sinhronem programskem jeziku je obravnavanje napak zelo enostavno. Če pride do napake, ko se izvaja stavek, bo vrgel napako in program jo lahko ujame.
Na kratko, sinhrono programiranje ima dve glavni funkciji, tj. eno samo opravilo se izvaja naenkrat, naslednji niz naslednjih opravil pa bo obravnavan šele, ko bo trenutno opravilo končano. Pri tem sledi zaporedni izvedbi kode.
To vedenje programiranja, ko se izvaja stavek, jezik ustvari situacijo blokovne kode, saj mora vsako opravilo čakati na dokončanje prejšnjega opravila.
Toda ko ljudje govorijo o JavaScriptu, je bil vedno zmeden odgovor, ali je sinhroni ali asinhroni.
V zgoraj obravnavanih primerih, ko smo funkcijo uporabili kot povratni klic v funkciji filtra, je bila izvedena sinhrono. Zato se imenuje sinhrona izvedba. Funkcija filtra mora počakati, da se večja funkcija konča z izvajanjem.
Zato se funkcija povratnega klica imenuje tudi blokiranje povratnih klicev, saj blokira izvajanje nadrejene funkcije, v kateri je bila priklicana.
Primarno se JavaScript šteje za enonitnega sinhronega in blokirajočega značaja. Toda z uporabo nekaj pristopov lahko omogočimo, da deluje asinhrono na podlagi različnih scenarijev.
Zdaj pa poglejmo asinhroni JavaScript.
Asinhroni JavaScript
Asinhroni programski jezik se osredotoča na izboljšanje zmogljivosti aplikacije. V takšnih scenarijih je mogoče uporabiti povratne klice. Asinhrono vedenje JavaScripta lahko analiziramo s spodnjim primerom:
function greet(){ console.log('greet after 1 second') } setTimeout(greet, 1000)
Iz zgornjega primera funkcija setTimeout kot argumenta sprejme povratni klic in čas v milisekundah. Povratni klic se prikliče po omenjenem času (tukaj 1s). Na kratko, funkcija bo na izvedbo čakala 1 s. Zdaj pa si oglejte spodnjo kodo:
function greet(){ console.log('greet after 1 second') } setTimeout(greet, 1000) console.log('first') console.log('Second')
Izhod:
first Second greet after 1 second
Iz zgornje kode bodo najprej izvedena sporočila dnevnika po setTimeoutu, medtem ko bo časovnik minil. Posledica tega je ena sekunda in nato pozdravno sporočilo po 1 sekundnem časovnem intervalu.
V JavaScriptu je setTimeout asinhrona funkcija. Kadarkoli pokličemo funkcijo setTimeout, ta registrira funkcijo povratnega klica (v tem primeru greet), ki naj se izvede po določeni zakasnitvi. Vendar pa ne blokira izvajanja naslednje kode.
V zgornjem primeru so sporočila dnevnika sinhroni stavki, ki se izvedejo takoj. Niso odvisni od funkcije setTimeout. Zato izvajajo in beležijo svoja sporočila v konzolo, ne da bi čakali na zakasnitev, določeno v setTimeout.
Medtem pa zanka dogodkov v JavaScriptu obravnava asinhrone naloge. V tem primeru počaka, da mine podana zakasnitev (1 sekunda), in po preteku tega časa prevzame funkcijo povratnega klica (greet) in jo izvede.
Tako se je druga koda po funkciji setTimeout izvajala med izvajanjem v ozadju. To vedenje omogoča JavaScriptu, da izvaja druge naloge, medtem ko čaka na dokončanje asinhrone operacije.
Za obravnavo asinhronih dogodkov v JavaScriptu moramo razumeti sklad klicev in čakalno vrsto povratnih klicev.
Razmislite o spodnji sliki:
Iz zgornje slike je tipičen motor JavaScript sestavljen iz pomnilnika kopice in sklada klicev. Klicni sklad izvede vso kodo brez čakanja, ko je potisnjen v sklad.
Pomnilnik kopice je odgovoren za dodeljevanje pomnilnika za objekte in funkcije med izvajanjem, kadar koli so potrebni.
Zdaj so naši motorji brskalnika sestavljeni iz več spletnih API-jev, kot so DOM, setTimeout, konzola, pridobivanje itd., in motor lahko dostopa do teh API-jev z uporabo globalnega objekta okna. V naslednjem koraku nekatere zanke dogodkov igrajo vlogo vratarja, ki izbira funkcijske zahteve znotraj čakalne vrste za povratni klic in jih potisne v sklad. Te funkcije, kot je setTimeout, zahtevajo določen čas čakanja.
Zdaj pa se vrnimo k našemu primeru, funkciji setTimeout; ko naleti na funkcijo, se časovnik registrira v čakalni vrsti za povratni klic. Po tem se preostala koda potisne v klicni sklad in se izvede, ko funkcija doseže svojo časovno omejitev, se izteče in čakalna vrsta povratnega klica potisne funkcijo povratnega klica, ki ima podano logiko in je registrirana v funkciji časovne omejitve . Tako bo izvedena po določenem času.
Scenariji pekla s povratnim klicem
Zdaj smo razpravljali o povratnih klicih, sinhronih, asinhronih in drugih pomembnih temah za pekel povratnih klicev. Razumejmo, kaj je pekel povratnega klica v JavaScriptu.
Situacija, ko je ugnezdenih več povratnih klicev, je znana kot pekel povratnega klica, saj je njegova oblika kode videti kot piramida, ki ji pravijo tudi 'piramida pogube'.
Zaradi povratnega klica je težje razumeti in vzdrževati kodo. To situacijo lahko večinoma vidimo med delom v vozlišču JS. Na primer, upoštevajte spodnji primer:
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... } })
V zgornjem primeru getUserData prevzame uporabniško ime, ki je odvisno od seznama člankov ali pa mora izvleči odgovor getArticles, ki je znotraj članka. getAddress ima tudi podobno odvisnost, ki je odvisna od odziva getUserData. Ta situacija se imenuje pekel povratnega klica.
Notranje delovanje pekla povratnega klica je mogoče razumeti s spodnjim primerom:
10 ml je koliko
Razumejmo, da moramo izvesti nalogo A. Za izvedbo naloge potrebujemo nekaj podatkov iz naloge B. Podobno; imamo različne naloge, ki so odvisne ena od druge in se izvajajo asinhrono. Tako ustvari niz funkcij povratnega klica.
Razumejmo obljube v JavaScriptu in kako ustvarjajo asinhrone operacije, kar nam omogoča, da se izognemo pisanju ugnezdenih povratnih klicev.
JavaScript obljublja
V JavaScriptu so bile obljube predstavljene v ES6. Je objekt s sintaktično prevleko. Zaradi svojega asinhronega obnašanja je alternativni način, da se izognete pisanju povratnih klicev za asinhrone operacije. Dandanes se spletni API-ji, kot je fetch(), izvajajo z obetavnim, ki zagotavlja učinkovit način dostopa do podatkov s strežnika. Izboljšala je tudi berljivost kode in je način, kako se izogniti pisanju ugnezdenih povratnih klicev.
Obljube v resničnem življenju izražajo zaupanje med dvema ali več osebami in zagotovilo, da se bo določena stvar zagotovo zgodila. V JavaScriptu je Promise objekt, ki zagotavlja izdelavo ene same vrednosti v prihodnosti (če je to potrebno). Promise v JavaScriptu se uporablja za upravljanje in reševanje asinhronih operacij.
Obljuba vrne objekt, ki zagotavlja in predstavlja dokončanje ali neuspeh asinhronih operacij in njihov izhod. Je približek vrednosti, ne da bi poznal točen rezultat. Koristno je, da asinhrona dejanja zagotovijo morebitno vrednost uspeha ali razlog za neuspeh. Tako asinhrone metode vrnejo vrednosti kot sinhrone metode.
Na splošno imajo obljube naslednja tri stanja:
- Izpolnjeno: Izpolnjeno stanje je, ko je bilo uporabljeno dejanje razrešeno ali uspešno zaključeno.
- V čakanju: stanje v čakanju je, ko je zahteva v obdelavi in uveljavljeno dejanje ni bilo niti razrešeno niti zavrnjeno in je še vedno v začetnem stanju.
- Zavrnjeno: Zavrnjeno stanje je, ko je bilo uporabljeno dejanje zavrnjeno, zaradi česar želena operacija ni uspela. Vzrok za zavrnitev je lahko karkoli, tudi nedelovanje strežnika.
Sintaksa za obljube:
let newPromise = new Promise(function(resolve, reject) { // asynchronous call is made //Resolve or reject the data });
Spodaj je primer pisanja obljub:
To je primer pisanja obljube.
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))
V zgornjem primeru lahko vidimo, kako lahko učinkovito uporabimo obljube za zahtevo s strežnika. Opazimo lahko, da je berljivost kode večja v zgornji kodi kot pri povratnih klicih. Obljube zagotavljajo metode, kot sta .then() in .catch(), ki nam omogočajo, da obravnavamo status operacije v primeru uspeha ali neuspeha. Določimo lahko primere za različna stanja obljub.
Async/Await v JavaScriptu
To je še en način, da se izognete uporabi ugnezdenih povratnih klicev. Async/Await nam omogoča veliko učinkovitejšo uporabo obljub. Lahko se izognemo veriženju metod .then() ali .catch(). Te metode so odvisne tudi od funkcij povratnega klica.
Async/Await je mogoče natančno uporabiti s Promise za izboljšanje delovanja aplikacije. Interno je rešil obljube in zagotovil rezultat. Poleg tega je spet bolj berljiv kot metodi () ali catch().
Async/Await ne moremo uporabiti z običajnimi funkcijami povratnega klica. Če ga želimo uporabiti, moramo funkcijo narediti asinhrono tako, da pred ključno besedo funkcije napišemo ključno besedo async. Vendar interno uporablja tudi veriženje.
Spodaj je primer 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();
Če želite uporabiti Async/Await, mora biti funkcija podana s ključno besedo async, ključna beseda await pa mora biti zapisana znotraj funkcije. Async bo ustavil svoje izvajanje, dokler obljuba ni razrešena ali zavrnjena. Nadaljevalo se bo, ko bo obljuba podeljena. Ko je razrešen, bo vrednost izraza await shranjena v spremenljivki, ki jo vsebuje.
Povzetek:
Na kratko, ugnezdenim povratnim klicem se lahko izognemo z uporabo obljub in async/await. Poleg teh lahko sledimo tudi drugim pristopom, kot je pisanje komentarjev, v pomoč pa je lahko tudi razdelitev kode na ločene komponente. Toda dandanes razvijalci raje uporabljajo async/await.
Zaključek:
Pekel povratnega klica v JavaScriptu se imenuje situacija, ko se izvaja prevelika količina ugnezdenih funkcij povratnega klica. Zmanjša berljivost kode in vzdrževanje. Situacija povratnega klica se običajno pojavi, ko se ukvarjate z asinhronimi operacijami zahtev, kot je izdelava več zahtev API ali obravnava dogodkov s kompleksnimi odvisnostmi.
Za boljše razumevanje pekla povratnega klica v JavaScriptu.
JavaScript vse obravnava kot objekt, kot so nizi, nizi in funkcije. Zato nam koncept povratnega klica omogoča, da funkcijo posredujemo kot argument drugi funkciji. Funkcija povratnega klica bo najprej dokončala izvedbo, nadrejena funkcija pa se bo izvedla pozneje.
Funkcije povratnega klica se izvajajo asinhrono in omogočajo, da se koda nadaljuje, ne da bi čakala na dokončanje asinhrone naloge.