- Async/await oslanja se na obećanja kako bi ponudio čitljiviji asinhroni kod, bliži tradicionalnom sinkronom stilu.
- Asinkrone funkcije uvijek vraćaju obećanje i omogućuju vam korištenje naredbe await za pauziranje njihovog izvršavanja bez blokiranja glavne niti.
- Rješavanje grešaka pomoću try/catch i uslužnih programa poput Promise.all olakšava kontrolu grešaka i kombiniranje nekoliko operacija paralelno.
- Kombinirana upotreba obećanja, async/await i API-ja poput fetcha temelj je modernog mrežno orijentiranog JavaScript razvoja.
La programiranje asinkrono u JavaScriptu postao je nezamjenjiv u modernom web razvoju: API zahtjevi, pristup baza podatakaČitanje datoteke ili bilo koja operacija koja traje dulje od trenutka ovisi o tome kako bi sučelje radilo glatko. Zahvaljujući obećanja, asinkronost/čekanje i API-ji Imamo vrlo moćan skup alata za kontrolu tog toka bez blokiranja aplikacije.
Godinama se uzorak uglavnom koristio povratne funkcije i ulančana obećanjakoji, iako vrlo dobro funkcioniraju, mogu završiti u zloglasnom "paklenom povratnom pozivu" ili u beskrajnim lancima .then() y .catch() teško pratiti. Dolaskom asinkroniziranje/čekanje u ECMAScript 2017JavaScript je napravio ogroman skok u čitljivosti, omogućujući pisanje asinhronog koda u stilu gotovo identičnom tradicionalnom sinkronom kodu.
Što je asinhrono programiranje u JavaScriptu i zašto je toliko važno?
La asinhrono programiranje To je način organiziranja koda tako da zadatak može započeti i program može nastaviti izvršavati druge stvari bez da se "zamrzne" čekajući odgovor. U JavaScriptu je to ključno jer se jezik izvodi, u pregledniku i u Node.js-u, na jedna glavna nit koji se odnosi i na logiku i na sučelje.
Kada baciš HTTP zahtjev API-juKada čitate datoteku ili šaljete upit bazi podataka, ove operacije mogu trajati od milisekundi do nekoliko sekundi. Da su sinkrone, korisnik bi vidio da je stranica zaključana i da ne može komunicirati. S asinkronim modelom, JavaScript mehanizam delegira ove zadatke i može nastaviti raditi u međuvremenu. slikanje korisničkog sučelja, rukovanje događajima i pokretanje drugih skripti.
Izvorno, standardni mehanizam za reagiranje kada je operacija završila bio je prosljeđivanje funkcija povratnog poziva kao argument. Ta je funkcija kasnije izvršena s rezultatom ili greškom. Problem je u tome što se, lančanjem povratnih poziva unutar drugih povratnih poziva, strašni pakao povratnih poziva: kod s previše uvlačenja, teško čitljiv, teško ga je otkloniti i održavati.
Kako bi se riješio dio tog kaosa, pojavilo se sljedeće: obećanjakoji obuhvaćaju rezultat asinhrone operacije i omogućuju lančano povezivanje akcija s .then() i upravljati neuspjesima s .catch()To je već bilo veliko poboljšanje, ali je i dalje ostavilo kod pun povratnih poziva "prikriven" iza tih metoda.
Obećanja: temelji async/await-a
Prije nego što shvatimo async/await, bitno je biti vrlo jasan oko toga što je obećanje u JavaScriptuObećanje je objekt koji predstavlja budući rezultat asinhrone operacije: nešto što se još nije dogodilo, ali će se dogoditi kasnije, bilo uspješno ili neuspješno.
Una standardno obećanje može se naći u jednom od tri dobro definirana stanja: na čekanju (u tijeku) dok je operacija u tijeku, završeno (ispunjeni) kada dobro završi i proizvede vrijednost ili bude odbijeno (odbijen) kada ne uspije i generira grešku. To stanje obećanja određuje koji će se povratni pozivi izvršiti sljedeći.
Kada kreirate obećanje koristeći konstruktor PromisePredajete mu izvršnu funkciju koja prima dva argumenta: resolve y rejectAko operacija prođe dobro, zoveš resolve(valor)Ako se pojavi problem, pozivate se reject(error)Zatim, izvana, možete koristiti miPromesa.then() obraditi rezultat i .catch() za upravljanje greškama.
Osim ručno izrađenih obećanja, mnogi moderni jezični API-ji već Ispunili su serijsko obećanje., funkcija fetch()Na primjer, pokreće HTTP zahtjev i odmah vraća obećanje koje će se na kraju riješiti objektom. Response ili odbijeno ako dođe do kvara mreže.
Za paralelni rad s više zadataka, JavaScript nudi alate kao što su Promise.all()Ova metoda uzima niz obećanja i vraća novo obećanje koje se rješava samo kada su sva obećanja uspješno završena ili se odbacuje ako bilo koje od njih ne uspije. Ovo je posebno korisno kada želite kombinirati podatke iz različitih izvora prije daljnjeg.
Ključni koncepti: asinkronost, čekanje i model pogreške
Sintaksa async/await je izgrađen na obećanjimaNe zamjenjuje ih. Ono što čini jest pruža praktičniji, jasniji način rada s njima, bliži sinkronom programiranju, smanjujući vizualnu buku... .then() okovan lancima.
Ključna riječ async Postavlja se prije funkcije kako bi se JavaScript mehanizmu reklo da će funkcija raditi asinkrono i da, što god se dogodi unutra, uvijek će održati obećanjeČak i ako vratite normalnu vrijednost, motor će je automatski upakirati u Promise.resolve(valor).
Sa svoje strane, rezervirana riječ await može se koristiti samo unutar funkcije deklarirane kao asyncNjegova je funkcija pauzirati izvršavanje unutar te funkcije dok se ne ispuni obećanje koje mu je dano. je li riješeno ili odbijeno, izravno vraćajući riješenu vrijednost ili bacajući iznimku ako obećanje završi s greškom.
kada koristite await promesa i obećanje se izvrši, dobit ćete rezultat kao da je riječ o sinkronom pozivu. Ako se umjesto toga obećanje odbije, U tom trenutku se generira iznimka., što je ekvivalentno činjenju throw errorTo omogućuje ponovnu upotrebu istih struktura za obradu grešaka kao u sinkronom kodu, kao što je try..catch.
Omatanjem naših asinhronih poziva u blok try { ... } catch (error) { ... }Možemo uhvatiti slučajeve u kojima bilo koje od obećanja ne uspije, prikazati odgovarajuće poruke korisniku ili donijeti odluke poput ponovnog pokušaja, preusmjeravanja ili zapisivanja neuspjeha u vanjski sustav.
Ključna riječ async: funkcije koje uvijek vraćaju obećanja
mjesto async ispred funkcije To ima dvije vrlo važne posljedice: s jedne strane, prisiljava povratnu vrijednost da uvijek bude obećanje; s druge strane, omogućuje korištenje await unutar tijela te funkcije, što potpuno mijenja strukturu asinhronog toka.
Ako deklarirate nešto poput async function sumar() { return 1; }Iako se može činiti kao normalna funkcija, ono što zapravo dobivate je funkcija koja vraća prethodno riješeno obećanje s hrabrošću 1Izvan vašeg sustava, morat ćete pristupiti tom rezultatu pomoću await sumar() o kon sumar().then() ako više volite lančani stil.
Također možete eksplicitno vratiti obećanje iz funkcije. async, na primjer return Promise.resolve(1);i ponašanje će biti isto. U oba slučaja, funkcija je usklađena s modelom obećanja, što olakšava njezinu kombinaciju s drugim asinhronim funkcijama i uslužnim programima kao što su Promise.all().
Vrlo je često da, kada počinjemo, zaboravimo označiti funkciju kao takvu. async a ipak koristimo await dentroU toj situaciji, JavaScript će izbaciti sintaktičku grešku koja ukazuje na to da await Ovo vrijedi samo u asinhronim funkcijama (ili u modulima koji to podržavaju na višoj razini). Ova poruka je podsjetnik da async y await uvijek idu ruku pod ruku.
U okruženjima gdje ne koristimo module ili moramo podržavati starije preglednike, postoji široko rasprostranjen obrazac koji se sastoji od omotajte kod unutar samostalno izvršavajuće asinkrone funkcijenešto poput (async () => { ... })();Na taj način, cijeli blok ostaje unutar asinhronog konteksta bez "kontaminacije" globalnog prostora.
Kako await funkcionira i što zapravo radi s obećanjima
Prava promjena načina razmišljanja dolazi s awaitKada JavaScript mehanizam naiđe na const resultado = await algunaPromesa; unutar funkcije async, pauzirati izvršavanje te funkcije u tom trenutku i vraća se u petlju događaja kako bi nastavio obavljati druge zadatke.
U međuvremenu, obećanje nastavlja svoj tok neovisno. Kada se konačno riješi, mehanizam nastavlja izvršavanje funkcije odmah nakon retka s await, dodjeljivanje varijabli vrijednost koju vraća obećanjeIz perspektive nekoga tko čita kod, izgleda kao sinkroni poziv koji samo traje malo duže.
Zanimljivo je da ova pauza Ne blokira CPUGlavna JavaScript nit može obrađivati korisničke događaje, animacije, drugi asinhroni kod itd. Zato kažemo da await To je više od elegantna sintaktička apstrakcija o obećanjima novog načina izvršavanja zadataka u pozadini.
Ako želite, možete zamisliti tu kombinaciju const resultado = await promesa; es promesa.then(valor => { resultado = valor; ... }) nečeg ekvivalentnog, ali distribuiranog na linearan način koji izbjegava ugniježđene povratne funkcije i poboljšava jasnoću toka.
Jedan zanimljiv detalj je da, ako se preselite u await objekt koji nije obećanje, ali ima metodu .then()Motor će pokušati tretirati kao da jest. Pozvat će se na to .then() s dvije unutarnje funkcije rezolucija i odbacivanje vrlo slične onima graditelja Promise. Na ovaj način, await Kompatibilan je s implementacijama obećanja tipa "thenable".
Rješavanje grešaka s async/await: try/catch i odbijena obećanja
Kada je obećanje odbiti dok koristite awaitIzravni učinak je da se u toj liniji baca iznimka. Iz perspektive vašeg koda, to je kao da ste napisali throw error; upravo tamo gdje await, što uzrokuje da izvršenje skoči na blok catch najbliži.
Na primjer, ako koristite const datos = await obtenerDatos(); i funkcija obtenerDatos Vraća obećanje koje završava greškom; tok će ići direktno na blok catch (error) od vas try..catchAko nemate blok za snimanje oko njega, implicitno obećanje da funkcija vraća async Bit će odbijeno.
To znači da, iako unutra još uvijek mogu biti obećanja, kada radite s async/wait prirodan način rješavanja grešaka to je s try..catchkao i u bilo kojem drugom JavaScript kodu koji baca iznimke. Ovo ujedinjenje stilova uvelike pojednostavljuje logiku i smanjuje potrebu za pamćenjem različitih obrazaca ovisno o tome je li kod sinkroni ili asinkroni.
Ako greškom zaboravite dodati .catch() obećanju koje vraća funkcija async Ako ne uspije, okruženje će prikazati upozorenje. „neupravljano obećanje“ (neobrađeno odbijanje obećanjaOsim toga, možete koristiti globalni događaj u pregledniku. unhandledrejection registrirati program koji bilježi sve te pogreške i sprječava da ostanu nezapažene.
Kada kombinirate await s komunalnim uslugama kao što su Promise.all, jedna greška u jednom od obećanja će širi zajedničko obećanje i konačno rezultira iznimkom koju možete uhvatiti pomoću try..catchNa taj način upravljanje greškama u složenim tijekovima rada ostaje dosljedno i centralizirano.
Asinkroniziranje/čekanje s dohvaćanjem: detaljni API pozivi
Jedna od najčešćih upotreba async/await je pristup HTTP API-ji koji koriste funkciju fetch()Ovaj API vraća obećanje koje se rješava u objekt Response, na kojem zatim možete pozvati metode kao što su .json() o .text()što zauzvrat donosi nova obećanja.
Zamislite da želite enkapsulirati logiku zahtjevanja podataka s krajnje točke u funkciji. Ako deklarirate async function obtenerDatos(), unutar njega možete pričekajte odgovor mreže s await fetch(...) a zatim transformirajte tijelo u JSON s const datos = await respuesta.json(); bez potrebe za lančanim povezivanjem nekoliko .then().
Kako bi se osiguralo da je poslužitelj ispravno odgovorio, uobičajeno je provjeriti svojstvo respuesta.okšto označava je li HTTP statusni kod u rasponu od 200 do 299. Ako nije, možete ručno izbaciti grešku s throw new Error('Error en la red');, što će uzrokovati catch uhvatiti situaciju.
Organizirajte cijeli tok unutar try { ... } catch (error) { ... } Zbog toga je asinhrona funkcija mnogo otpornija: bilo koji problem, bilo da se radi o mreži, parsiranju JSON-a ili logici, pokrenut će blok snimanja gdje možete zabilježi grešku ili prikaži poruku prilagođeniju korisniku.
U konačnici, da biste koristili funkciju, jednostavno je pozovite kao i bilo koju drugu: obtenerDatos();Imajte na umu da, budući da je asinhroni, vraća obećanje, pa ćete izvana morati odabrati hoćete li ga koristiti. await obtenerDatos() unutar druge funkcije async ili se time pozabaviti .then().catch() ako se nalazite u neasinkronom kontekstu.
Izvršite više operacija paralelno s Promise.all i async/await
Često ne želite čekati da se jedan zahtjev završi prije nego što započnete sljedeći; umjesto toga, zainteresirani ste za... pokrenuti više asinhronih operacija odjednom i nastaviti kada svi završe. Za to, metoda Promise.all() savršeno pristaje.
Pretpostavimo da trebate koristiti dva različita API-ja, na primjer /datos1 y /datos2, i kasnije spojiti informacije u jedan objektUmjesto da čekate da se prvi završi prije nego što započnete drugi, možete napisati nešto poput const [r1, r2] = await Promise.all([fetch(url1), fetch(url2)]); i obje prijave bit će podnesene paralelno.
Nakon što dobijete oba odgovora, možete se prijaviti await na svakom od njih kako bi ih pretvorili u JSON: const d1 = await r1.json(); const d2 = await r2.json();Konačno, vrlo je uobičajeno kombinirati podatke s operatorom propagacije: const datosCombinados = { ...d1, ...d2 };, što stvara jedinstvenu strukturu koja spaja svojstva oba.
Cijeli ovaj blok treba biti omotan u try..catchjer će svaki neuspjeh u jednom od zahtjeva uzrokovati Promise.all() je odbijen. Zahvaljujući async/await, rukovanje ovakvim scenarijima s istodobne operacije Vizualno je jednostavan, ali vrlo moćan u smislu performansi.
Ovaj obrazac je posebno koristan kada vaša web aplikacija zahtijeva nekoliko neovisnih resursa prije nego što se prikaz može prikazati: na primjer, istovremeno dobivanje konfiguracije, korisničkih podataka i statistike korištenja, smanjujući ukupno vrijeme čekanja.
Prave prednosti async/await u odnosu na "čista" obećanja i povratne pozive
El uso async/await nudi niz praktičnih prednosti Ove su prednosti vidljive od najranijih primjera do velikih primjena. Najočitija je čitljivost: tijek se piše od vrha prema dnu bez potrebe za ugniježđenim ili ulančanim funkcijama. .then() jedan za drugim.
Još jedna jaka strana je održivost kodaS više linearnih blokova lakše je uvoditi promjene, izdvajati dijelove u pomoćne funkcije ili ponovno koristiti logiku. To izravno rezultira manjim brojem pogrešaka i glatkijom krivuljom učenja za nove korisnike u projektu.
Obrada grešaka s try..catch umjesto višestrukih .catch() raspršena Pomaže centralizirati logiku kvara. Možete okružiti nekoliko await s jednim blokom i implementirati dosljednu politiku upravljanja greškama, bez gubitka detalja o tome u kojoj se liniji problem pojavio.
S gledišta performansi, async/await sam po sebi ne ubrzava kod, ali vas može potaknuti na korištenje obrazaca poput Promise.all() paralelno izvršavati zadatke tamo gdje ste ih prethodno možda izvršavali serijski. Rezultat je manje ukupno vrijeme čekanja za krajnjeg korisnika.
Nadalje, async/await je kompatibilno s tradicionalnim obećanjimaNe morate prepisati sav kod da biste ga usvojili. Možete kombinirati funkcije koje ga koriste. .then() s drugima koji koriste await Nema problema, što olakšava postupnu migraciju projekata.
U modernom ekosustavu, kako u preglednicima tako i u Node.js-u, većina alata i biblioteka već se oslanja na obećanja i async/await. Zbog toga dobro učenje ovih koncepata nije opcionalno, već neophodno. uvjet za rad s modernim JavaScriptom u profesionalnim projektima.
Cijeli ovaj skup ideja - obećanja, asinkronost/čekanje, fetch, Promise.allMetoda "pokušaj/uhvati" i glosar ključnih pojmova čine vrlo čvrst okvir za pisanje jasan, čitljiv i robustan asinhroni kodNjihovo savladavanje omogućuje vam izradu modernih web aplikacija koje brzo reagiraju, dobro iskorištavaju mrežu i puno ih je lakše održavati tijekom vremena.
Strastveni pisac o svijetu bajtova i tehnologije općenito. Volim dijeliti svoje znanje pisanjem, a to je ono što ću učiniti na ovom blogu, pokazati vam sve najzanimljivije stvari o gadgetima, softveru, hardveru, tehnološkim trendovima i još mnogo toga. Moj cilj je pomoći vam da se snađete u digitalnom svijetu na jednostavan i zabavan način.