Uvod u Fetch API
Uvod
U prošlom članku (koji je za temu imao uvod u AJAX), najavili smo da će tema narednog članka biti uvod u Fetch API, a u članku o asinhronom programiranju u JavaScript-u (čiji je veći deo posvećen tematici obrade asinhronih zahteva preko 'promisa'), najavili smo da ćemo u doglednoj budućnosti objaviti tutorijal koji prikazuje korišćenje promisa u praksi.
Ukoliko ste dobro razumeli tematiku AJAX-a i promisa, upoznavanje sa Fetch API zahtevima biće prilično jednostavno, jer može se reći da Fetch API predstavlja svojevrsnu kombinaciju dva pomenuta pristupa.
Da bismo se što bolje upoznali sa najavljenim temama, odlučili smo da 'spojimo lepo sa korisnim': u jednom članku objedinićemo uvodni tekst o Fetch API tehnikama, i mini-tutorijal koji odmah na delu prikazuje kako se preko promisa može osposobiti dinamički sajt koji učitava podatke sa servera.
Fetch API = AJAX & Promise?
Na šta mislimo kada kažemo da je Fetch API (u idejnom smislu), kombinacija pristupa sa kakvim smo se sreli pri upoznavanju sa AJAX-om, i promisa:
- u pitanju je sistem za slanje asinhronih zahteva * i obradu primljenih podataka
- Fetch API je implementiran preko promisa
Sintaksa koju koristi Fetch API, jednostavnija je od 'tradicionalnog' AJAX-a / XMLHttpRequest
objekata, i u osnovi svega je funkcija fetch
kojoj se kao argument predaje URL (tipično, URL iza koga 'stoji' skripta za obradu podataka, koja kao rezultat obrade vraća običan tekst, JSON objekat ili HTML (ali, po potrebi, mogu se koristiti i drugi formati)) ....
Povratna vrednost funkcije fetch
je promis, koji, pored sadržaja koje pozvani URL vraća direktno, sadrži i dodatne parametre (kao što je HTTP status direktnog odgovora servera i sl).
Postupak u obradi Fetch zahteva podrazumeva da se do traženog sadržaja obrade URL-a dolazi (pomalo) posredno, ali, i ostale informacije koje server vraća, svakako su dobrodošle, i stoga se može reći da je u pitanju više nego prihvatljivo rešenje.
Na sledećoj slici ....
.... vidimo tipičan proces obrade (pri čemu je, zarad preglednosti, izostavljena naredba catch
koju ćemo u nastavku koristiti).
Dodatni parametri koji su deo direktnog odgovora funkcije fetch
, mogu se koristiti (npr) ako je potrebno uveriti se da pri rešavanju zahteva nije došlo do grešaka (recimo, potrebno je da se nedvosmisleno uverimo da je server vratio HTTP status 200 i sl):
Za dobijanje tekstualnog ili drugog sadržaja koji je vraćen i upisan u prvi odgovor (prosto rečeno - da bismo dobili 'ono što smo zapravo tražili'), može se koristiti kod po sledećem obrascu:
U pitanju je posrednički promis koji, iz direktnog odgovora servera (koji sadrži dodatne informacije), 'izvlači' sadržaj koji smo direktno tražili, a u svemu navedenom, JSON je samo jedan od mogućih formata sadržaja.
Najčešće korišćene funkcije su:
.json()
- ukoliko odgovor servera sadrži JSON objekat.text()
- ukoliko odgovor servera sadrži običan tekst.blob()
- ukoliko odgovor servera sadrži binarnu datoteku (slike i dr.)
Sada, kada su nam poznati svi koraci koje je potrebno preduzeti, pogledajmo primer tipičnog fetch
poziva, preko koga se JSON sadržaj koji je dobijen od servera, ispisuje u konzoli (pri čemu se koristi i naredba catch
koja reaguje u slučaju pojave greške):
Na ovom mestu ponovo vidimo da zahtev - ukoliko ne dođe do greške - tipično prolazi kroz tri faze:
- upućivanje zahteva
- čitanje JSON objekta iz rezultata koji se dobija kao direktan odgovor od servera
- obrada JSON objekta
Na kraju, ako je potrebno 'ukomponovati' sve što smo do sada pominjali (proveru HTTP statusa direktnog odgovora i obradu primljenog JSON objekta), možemo to učiniti na sledeći način:
Sve što smo do sada videli, odnosilo se na zahteve koji ne sadrže dodatne parametre, ali, ima i situacija kada je sam poziv - nešto komplikovaniji (doduše, nećemo se takvim situacijama previše baviti u članku, osim u narednom poglavlju) ....
Slanje Fetch zahteva sa dodatnim parametrima
Fetch zahtevi koji su prikazani do sada, podrazumevaju čitanje podataka, međutim, za slanje POST zahteva (što praktično podrazumeva upis podataka na server), potrebno je proslediti dodatne parametre.
Dodatni parametri se predaju funkciji fetch
kao drugi argument:
Primera radi, ako je potrebno dodati novu poruku u tabelu, kod se može formatirati na sledeći način:
U ovom slučaju, odgovor se ne koristi direktno, kao do sada, ali, preko konzole ćemo biti obavešteni o tome da li je zadatak uspešno obavljen.
Slanje Fetch API zahteva preko async/await sintakse
Pošto smo se prethodno upoznali i sa pojednostavljenim načinom za pozivanje promisa, preko tzv. async/await
sintakse (koja, po svojim spoljnim odlikama, skoro u potpunosti deluje kao "običan", sinhroni programski kod), pokazaćemo kako se takav vid zapisa može koristiti za slanje Fetch zahteva (i, naravno, za obradu paketa podataka koji se dobija kao odgovor servera).
Zahtev za obradu može se uputiti prema sledećem obrascu:
Kao što je poznato, pojava rezervisane reči await
podrazumeva da se naredbe u nastavku neće izvršavati dok Fetch zahtev ne bude rešen.
U sledećem koraku, može se pozvati funkcija json()
, koja će vratiti JSON objekat sa podacima o korisniku (objekat ćemo nazvati upravo - korisnik
).
Ovoga puta, funkcija json()
se poziva preko objekta rez
, jer (da se podsetimo), objekat rez
, kao povratna vrednost funkcije fetch
- nije ništa drugo nego promis.
Da se podsetimo na još jedan detalj: budući da se u okviru async/await
sintakse metoda catch()
ne koristi, "hvatanje grešaka" potrebno je obaviti preko bloka try-catch
(prikazaćemo celokupan kod):
Na ovom mestu, vredi još jednom napomenuti šta se događa ukoliko zaboravimo rezervisanu reč await
, a događa se to da će program pokušati da pristupi promisu koji (još uvek) nije 'ispunjen' * i (najjednostavnije rečeno) - traženi podaci neće biti dostupni.
Primer sajta koji koristi fetch API
Pošto smo se pozabavili osnovnim karakteristikama Fetch API zahteva, razmotrićemo i jedan zanimljiv primer (preko koga ćemo se upoznati sa time kako u praksi funkcionišu tehnike o kojima smo pisali).
Primer možemo videti na sledećem formularu, preko koga se simulira učitavanje korisničkog naloga na sajtu sa oglasima (slobodno kliknite na dugme):
Ili, ako želite da posmatrate sajt u zasebnom 'jezičku' browsera, možete ispratiti sledeći link: Fetch Oglasi
(Može biti zgodnije za praćenje.)
Sajt koji smo videli, očigledno koristi jednu ili više tabela iz kojih se 'povlače' podaci: tabelama se upućuju zahtevi, primljeni podaci se obrađuju i prikazuju na stranici, i biće potrebno da se takve tabele kreiraju.
Međutim, da se prvo podsetimo: * na našem "ubačenom sajtu" (i sličnim sajtovima), učitavanje podataka se obavlja u etapama, što praktično znači da se određene funkcije pokreću samo ukoliko je rezultat prethodno izvršenih funkcija bio povoljan (na primer, ako autentifikacija korisnika nije "prošla", prekinuće se izvršavanje svih ostalih funkcija koje se tiču učitavanja oglasa; ukoliko je autentifikacija bila uspešna - ali nema oglasa ili komentara - neće se učitavati oglasi i komentari, i sl).
Naš glavni zadatak je da što bolje organizujemo metode koje su zadužene za različite etape učitavanja, ali - prvo ćemo pripremiti podatke.
Struktura tabela
Baza podataka koju koristimo, sastoji se iz četiri tabele, koje lako možete (re-)kreirati preko sledećih podataka (naravno, po želji možete i dodavati slogove):
Tabela korisnici
:
Tabela oglasi
:
Tabela poruke
:
Tabela online_prijatelji
:
Upit za čitanje podataka iz baze
Ovoga puta (budući da smo se u više navrata bavili učitavanjem podataka iz MySql baza preko PHP skripti), pustićemo vas da sami kreirate sve skripte za čitanje, s tim da ćemo vam ipak malo pomoći:
U tabeli korisnici
nalazi se samo jedan korisnik, * podaci se učitavaju na poznati način, ali, umesto da se podaci šalju u obliku HTML-a, biće direktno poslat JSON objekat, preko komande json_encode
(sa kojom smo se već upoznali u članku o AJAX-u).
(Ostale skripte potrebno je projektovati tako da vraćaju podatke na isti način.)
Skripta koju koristimo nalazi se na adresi:
https://www.codeblog.rs/primeri/fetch_oglasi/ucitavanje.php?podaci=korisnik
.... i mogu joj se predavati sledeći parametri preko URL-a:
.... što praktično znači da se odabir skripte koja će se na serveru pokretati, obavlja preko parametara upita (koji su deo URL-a).
JS skripta - Konfiguracija
Zarad što lakšeg pristupa HTML elementima i regulisanja intervala koji će kasnije biti korišćeni u funkcijama setTimer
, kreiraćemo sledeću konfiguraciju na početku:
Pomoćne funkcije
Funkcija ispisHTMLPolja
preuzima referencu na određeni HTML element i datom elementu dodeljuje tekstualni sadržaj i klasu (parametar polje
nije niska (tj. id), već element DOM strukture):
Iako funkcija uklanjanjeFormeZaPrijavu
praktično ima samo jednu liniju koda, svakako je elegantnije kada se u spoljnim delovima koda pojavi funkcija čiji naziv jasno sugeriše šta je namena funkcije:
Kada funkcija fetch
vrati rezultat, potrebno je formatirati primljene podatke (pretvoriti JSON u HTML), za šta ćemo koristiti pomoćne funkcije iz sledećeg odeljka.
Funkcije za formatiranje HTML-a
Funkcije za formatiranje HTML-a biće implementirane (kao i više puta do sada), preko šablonskih niski.
Funkcija za formatiranje podataka o korisniku
Funkcija formatiranjeKorisnikInfo
formatira prikaz osnovnih podataka o prijavljenom korisniku (prvi blok koji se pojavi posle uspešne prijave):
Pomoćne funkcije za formatiranje oglasa
Funkcija formatiranjeOglas
formatira pojedinačni oglas:
Funkcija formatiranjeOglasi
formatira spisak oglasa (preko funkcije formatiranjeOglas
):
Pomoćne funkcije za formatiranje poruka
Funkcija formatiranjePoruka
formatira pojedinačnu poruku:
Funkcija formatiranjePoruke
(množina), formatira listu poruka:
Pomoćne funkcije za formatiranje spiska prijatelja
Funkcija formatiranjeKorisnikPrijatelj
formatira podatke o pojedinačnom online prijatelju:
Funkcija formatiranjeKorisnikPrijatelji
(množina), formatira listu online prijatelja:
Sada smo spremni da kreiramo i promise.
Promisi za učitavanje podataka
Počnimo od promisa koji vraća podatke o korisniku (ukoliko ne dođe do grešaka u izvršavanju, podaci o korisniku biće vraćeni preko metode resolve
):
Struktura je složena i obuhvata (u praktičnom smislu), tri glavne celine (koje smo označili brojevima):
- (1) funkcija ....
- (2) unutar koje je promis ....
- (3) koji koristi
setTimeout
.... ali, već smo se susretali sa sličnim kodovima (i verujemo da ćete umeti da sagledate prikazanu strukturu u širem kontekstu).
Prava novost je unutrašnji deo:
.... i stoga ćemo izdvojenom delu posvetiti više pažnje:
- metoda
reject
(naravno, sa različitim porukama), pokreće se: i u slučaju da sama funkcijafetch
ne vrati rezultat, i u slučaju da korisnik ne unese ispravne podatke za prijavu - prikaz podataka o korisniku, očigledno prepuštamo nekoj drugoj konkretnoj metodi koja će biti implementirana u spoljnom delu koda (kada metoda
proveraKorisnikaInfo
vrati korektan rezultat, preko pozivaresolve(korisnik)
)
Sada možemo definisati i ostale promise (koji su jednostavniji, u tom smislu što nemaju dodatni uslov) ....
- Promis za proveru oglasa:
- Promis za proveru poruka:
- Promis za proveru podataka o online prijateljima:
Funkcije za prikaz učitanih podataka
Četiri promisa koje smo videli, naizgled "ne rade ništa", ali - vraćaju objekte, a objekti koje promisi vraćaju, prihvataju se i koriste za prikaz na stranici - preko zasebnih funkcija.
- Funkcija za učitavanje panela sa informacijama o prijavljenom korisniku:
- Funkcija za učitavanje oglasa (koje je prijavljeni korisnik objavio):
- Funkcija za učitavanje poruka (tj. komentara na oglase):
- Funkcija za učitavanje liste prijatelja koji su trenutno online:
Glavna funkcija za prijavu (implementirana preko async/await sintakse)
Budući da smo već razradili ideje i prikazali funkcije koje se pokreću preko glavne funkcije za prijavu korisnika, sama funkcija prijava
(koja se direktno poziva preko dugmeta sa početne stranice) ....
.... može se lako zapisati preko elegantne ES7 sintakse (kao asinhrona funkcija koja koristi blok try-catch
).
Naravno, kao i ranije (kada smo koristili async
funkcije), programski kod naizgled deluje kao prosto 'izlistavanje instrukcija', ali - ipak je u pitanju vezivanje promisa.
Za kraj ....
U 'toplim' letnjim mesecima (da ne kažemo pretoplim :)), tokom kojih mi pišemo ovakve članke, a vi čitate članke, nije lako održavati odgovarajuću radnu temperaturu 'aparata za razmišljanje i iznalaženje algoritama', međutim, ako ste raspoloženi za izazove, predlažemo (za vežbu), da probate da kreirate sajt koji ste videli - uz korišćenje promisa, ali - bez async/await sintakse.
Takođe može doći u obzir i neka 'zanimacija' kao što je samostalni pokušaj implementiranja sistema za autorizaciju i, naravno - bilo kakvo drugo unapređenje sajta koji je u ovom članku poslužio kao primer (koje vama pada na pamet).
Što se nas tiče, napravićemo manju pauzu po pitanju ozbiljnijeg gradiva, i stoga ćemo se u sledećih nekoliko nedelja baviti samo "laganijim" temama (kao što je, između ostalog, predstavljanje znakova i vremena na računarima), posle čega ćemo se vratiti na tematiku autorizacije korisnika ....