nav_dugme codeBlog codeBlog
  • početna Početna stranica
  • Sačuvani članci Sačuvani članci
  • Članci
     (spisak)
  • Kontakt
Povratak na vrh stranice

Info & povezani članci Info o članku - dugme

Info

trejler_sat Datum objave: 06.07.2021.

trejler_dokument Jezici: JavaScript

trejler_teg_narandzasti Težina: 7/10

JavaScript
es6
es7
http
http statusni kodovi
internet
ajax
fetch api
url
get
post
backend
teorija
tutorijali

Povezani članci

Asinhrono programiranje u JavaScriptuUvod u AJAX (Asynchronous JavaScript And XML)JSON - tekstualni format za razmenu podatakaJavaScript ES6 sintaksaUvod u PHP i back-end programiranjeUvod u Node.jsUvod u PythonŠablonske niske u programskim jezicimaCallback funkcije i lambda izraziJSON Web Token (JWT) - Struktura i primena u oblasti autorizacije web aplikacijaASCII, UNICODE i UTF - Predstavljanje znakova na računarimaUvod u JavaScript i DOM (Document Object Model)
Svi članci
On two occasions, I have been asked [by members of Parliament], 'Pray, Mr. Babbage, if you put into the machine wrong figures, will the right answers come out?' I am not able to rightly apprehend the kind of confusion of ideas that could provoke such a question.
Charles Babbage

Uvod u Fetch API

Facebook LinkedIn Twitter Viber WhatsApp E-mail
zoom_plus zoom_minus bookmark
početna > Članci > Teorija

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

* Fetch API ne koristi objekte klase XMLHttpRequest, ali, u praktičnom smislu, i ovoga puta je u pitanju svojevrstan "AJAX" (u doslovnom/izvornom značenju skraćenice): Fetch API je 'asinhroni JavaScript za slanje zahteva serveru', a što se tiče XML-a .... radi se o formatu koji, u praksi, ionako nije zastupljen ni u tradicionalnom AJAX-u (koji je implementiran preko objekata klase XMLHttpRequest).

Sintaksa koju koristi Fetch API, jednostavnija je od 'tradicionalnog' AJAX-a (sa objektima klase XMLHttpRequest): u osnovi svega je funkcija fetch, kojoj se kao argument predaje određeni URL, pri čemu se tipično radi o URL-u 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) ....

		
fetch(url)
		
	
Slika 1. - Opšta šema fetch zahteva (sa osnovnim opcijama).

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).

Iako pretpostavljamo da je čitaocima jasno, ipak ćemo dodatno pojasniti: ako se preko funkcije fetch serveru pošalje zahtev za (običnom) tekstualnom datotekom, dobija se sadržaj same datoteke (isti je slučaj i ako se zatraži slika, ili neka druga binarna datoteka).

Ako se pri pozivu funkcije fetch kao argument navede URL određene PHP skripte (ili URL iza koga stoji skripta koja je realizovana preko neke druge web tehnologije), pozvana skripta se prvo izvršava na serveru, a sve što skripta "ispiše", biće povratna vrednost (baš kao i kada smo koristili XMLHttpRequest/AJAX).

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 ....

Fetch API
Slika 2. - Šematski prikaz izvršavanja Fetch promisa.

.... prikazan je 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):

		
fetch(url)
	.then(rezultat => {
		if (rezultat.status == 200) {
			console.log("Podaci su uspešno preuzeti")
		}
		else {
			console.error("Došlo je do greške pri preuzimanju podataka!");
		}
	});
		
	
Slika 3. - Provera HTTP statusa, posle izvršavanja funkcije fetch.

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'), tipično se koristi sledeći obrazac:

		
fetch(url)
	.then(rez => rez.json())
		
	
Slika 4. - Tipičan drugi korak u korišćenju funkcije fetch: čitanje JSON objekta iz objekta koji se dobija kao direktan odgovor servera.

U pitanju je posrednički promis, koji - iz direktnog odgovora servera (koji sadrži i dodatne informacije), 'izvlači' sadržaj koji smo tražili, a u svemu navedenom, JSON je samo jedan od mogućih formata sadržaja.

Sve zavisi od toga šta je predviđeno kao povratna vrednost za URL koji se poziva (na serveru kome je upućen zahtev), i recimo da smo u konkretnom primeru koji razmatramo, pozivali URL koji vraća JSON objekat.

Naravno, uvek se mora koristiti funkcija koja odgovara formatu podataka koji su zatraženi od servera.

Najčešće korišćene funkcije su sledeće:

  • .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):

		
fetch(url)
	.then(rez => rez.json())
	.then(rez => {
		console.log(rez);
	})
	.catch(greska => {
		console.log(greska);
	})
		
	
Slika 5. - Šematski prikaz tipičnog poziva funkcije fetch (pretvaranje rezultata u JSON objekat i naknadno korišćenje JSON objekta, uz odeljak catch, koji 'hvata' greške).

Na ovom mestu ponovo vidimo da zahtev 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

* Tipično = obrada zahteva u slučaju da ne dođe do greške.

Ukoliko dođe do greške, obrada podataka je drugačija (nema obrade primljenog JSON objekta i sl, ali, sprovode se operacije vezane za obradu grešaka).

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:

		
fetch(url)
	.then(rez => {
		if (rez.status != 200) {
			throw "Greška sa odgovorom servera!";
		}
		return rez.json();
	})
	.then(rez => {
		console.log(rez);
	})
	.catch(greska => {
		console.log(greska);
	})
		
	
Slika 6. - Primer upotrebe funkcije fetch (provera statusa, pretvaranje rezultata u JSON objekat i naknadno korišćenje JSON objekta, uz odeljak catch, koji 'hvata' greške).

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 POST 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 predaju se funkciji fetch u svojstvu drugog argumenta:

		
fetch(url, opcije)
		
	
Slika 7. - Opšta šema fetch zahteva sa dodatnim opcijama.

Primera radi, ako je potrebno dodati novu poruku u tabelu (koja pripada bazi podataka na određenom serveru i sl), kod se može formatirati na sledeći način:

		
let id     = 1;
let uid    = "korisnik_00";
let poruka = document.getElementById("polje_poruka").value;

let paket = {
	id:     id,
	uid:    uid,
	poruka: poruka
}

// Ne dajte se zbuniti, gornji JSON objekat
// praktično ima sledeći sadržaj:

/*
let paket = {
	id:     1,
	uid:    "korisnik_00",
	poruka: "Dobar dan!"
}
*/

fetch(url, {
	method: 'POST',
	headers: {
		'Accept':       'application/json, text/plain, */*',
		'Content-type': 'application/json'
	},
	body: JSON.stringify(paket)
})
	.then(rez => rez.json())
	.then(rez => console.log(rez))
	.catch(greska => console.error(greska));
		
	
Slika 8. - Slanje POST zahteva preko funkcije fetch.

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:

		
const rez = await fetch(URL)
		
	
Slika 9. - Slanje Fetch zahteva preko async/await sintakse.

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.

Kao što takođe znamo, obrada (u pomenutoj situaciji) traje veoma kratko, ali, podaci ne bi bili dostupni ako bi sledeća naredba bila pokrenuta odmah nakon fetch zahteva.

U sledećem koraku, poziva se 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.

		
let korisnik = await rez.json()
		
	
Slika 10. - Pristup podacima o korisniku, preko funkcije json().

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):

		
try {
	let rez      = await Fetch(url)
	let korisnik = await rez.json()
}
catch (err) {
	console.error("Došlo je do sledećih grešaka:")
	console.error(err)
}
		
	
Slika 11. - Programski kod za obradu podataka (po ugledu na početni primer), realizovan preko async/await sintakse i bloka try-catch.

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.

* Ako već u sledećoj naredbi skripta pokuša da pristupi rezultatu, podataka za obradu neće biti, budući da je promis koji vraća funkcija Fetch još uvek u stanju obrade ("pending").

Pomalo slikovito, rezervisana reč await predstavlja "malo strpljenja" dok skripta čeka da se Fetch zahtev izvrši (a tipičan Fetch zahtev, kao što znamo od ranije, izvršava se vrlo kratko, i najčešće je teško primetiti da se vreme obrade uopšte "povećalo").

Ali - Fetch zahtev ipak ne biva rešen "istog trenutka".

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):

Oglasi
Poruke
Online prijatelji

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.)

Iako "postoji mogućnost" da su oglasi izmišljeni, sajt je u tehničkom smislu 'pravi', uz dva izuzetka:

  • autorizacija je samo simulirana (zarad preglednosti) *
  • koristili smo i ovoga puta funkciju setTimer, da produžimo vremena učitavanja podataka, da biste mogli bolje da sagledate vremenski tok operacija (inače bi se učitavanje obavilo naizgled trenutno, nalik na učitavanje obične HTML stranice, i nikako ne biste mogli da steknete utisak da "izvršavanje sledeće operacije zavisi od uspešnog izvršavanja prethodne")

* Još jednom apelujemo na vas da ne koristite neodgovarajuće metode za autorizaciju korisnika u produkcijskim uslovima (što mi činimo u članku isključivo zarad očuvanja preglednosti)!

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 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).

* Međusobna uslovljenost izvršavanja funkcija (pomenuta u gornjim pasusima), bila je jedna od glavnih tema članka o asinhronim pozivima u JS-u.

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:

		
id | korisnicko_ime | avatar       | datum_registracije | broj_oglasa
----------------------------------------------------------------------
 1 | "korisnik_00"  | "avatar.png" | "12.2.2009."       | 3
		
	
Slika 12. - Struktura tabele korisnici.

Tabela oglasi:

		
id | naslov           | tekst
--------------------------------------------------------------------------
 1 | Audi A4          | "Na prodaju Audi A4, 2007. godište ...." 
 2 | Sector 450       | "Časovnik na prodaju, neotpakovan, u kutiji ...."
 3 | Sabrana dela FMD | "Komplet od 20 knjiga, tvrdi povez ...."
		
	
Slika 13. - Struktura tabele oglasi.

Tabela poruke:

		
id | korisnik    | vreme                | tekst
------------------------------------------------------------------------
 1 | korisnik_47 | 17.06.2021. 17:54:21 | "Nudim 300 Evra za Audi ...."
 2 | korisnik_71 | 24.06.2021. 09:12:33 | "Da li biste poslali .... "
 3 | korisnik_23 | 25.06.2021. 21:25:48 | "Koliko troši Audi na 100Km?"
		
	
Slika 14. - Struktura tabele poruke.

Tabela online_prijatelji:

		
id | korisnicko_ime | avatar
--------------------------------------
 1 | korisnik_12    | "avatar_12.png"
 2 | korisnik_24    | "avatar_24.png"
 3 | korisnik_36    | "avatar_36.png"
		
	
Slika 15. - Struktura tabele 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:

		
<?php
	require_once('povezivanje.php');
	$id       = 1; 
	$upit     = "SELECT * FROM korisnici WHERE id=$id";
	$rezultat = mysqli_query($veza, $upit);

	$paket = json_encode(array());
		
	if (mysqli_num_rows($rezultat) == 1) {
		$red   = mysqli_fetch_assoc($rezultat);
		$paket = json_encode($red);
	}
	
	echo $paket;
?>
		
	
Slika 16. - Upit za čitanje podataka iz baze (ovoga puta rezultat se ne vraća u obliku HTML-a, već u vidu JSON objekta).

U tabeli korisnici nalazi se samo jedan korisnik, * i pri tom se podaci 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).

* Kao što je već pomenuto, autorizacija je samo simulirana (međutim, uskoro ćemo posvetiti ceo članak upravo temi autorizacije korisničkih naloga).

(Ostale skripte potrebno je projektovati tako da vraćaju podatke po istom principu.)

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:

?podaci=korisnik ?podaci=oglasi ?podaci=poruke ?podaci=prijatelji

.... što praktično znači da se odabir skripte koja će se na serveru pokretati, obavlja preko parametara upita (koji su deo URL-a).

Našu PHP skriptu možete koristi pri kreiranju primera, ali, svakako preporučujemo da ponovo napravite: i bazu, i ostale skripte (biće to dobra vežba). :)

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:

		
const forma_prijava = document.getElementById("forma_prijava");
const polje_uid     = document.getElementById("forma_prijava_uid");
const polje_lozinka = document.getElementById("forma_prijava_lozinka");

const polje_korisnik_info = document.getElementById("korisnik_info");
const polje_oglasi        = document.getElementById("korisnik_oglasi");
const polje_poruke        = document.getElementById("poruke");
const polje_prijatelji    = document.getElementById("korisnik_prijatelji");

const URL_KORISNIK   = `ucitavanje.php?podaci=korisnik`;
const URL_OGLASI     = `ucitavanje.php?podaci=oglasi`;
const URL_PORUKE     = `ucitavanje.php?podaci=poruke`;
const URL_PRIJATELJI = `ucitavanje.php?podaci=prijatelji`;

// Konfiguracija 1 (deluje prirodnije)

///*
let INTERVAL_KORISNIK_INFO = 400;
let INTERVAL_OGLASI        = 800;
let INTERVAL_PORUKE        = 1200;
let INTERVAL_PRIJATELJI    = 800;
		
	
Slika 17. - Konfiguracioni deo JS skripte.

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):

		
function ispisHTMLPolja(polje, tekst, klasa) {
	polje.innerHTML += tekst;
	polje.classList.add(klasa);
}
		
	
Slika 18. - Pomoćna funkcija za ispis HTML polja.

Iako funkcija uklanjanjeFormeZaPrijavu 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:

		
function uklanjanjeFormeZaPrijavu() {
	forma_prijava.innerHTML = "";
}
		
	
Slika 19. - Pomoćna funkcija za uklanjanje forme za prijavu.

Kada funkcija fetch vrati rezultat, potrebno je formatirati primljene podatke (što znači: 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 (praktično - prvi blok koji se pojavi posle uspešne prijave):

		
function formatiranjeKorisnikInfo(korisnik) {
	return `<div class='korisnik_info_levi'>
	<img id='korisnik_info_avatar' src='/slike/${korisnik.avatar}'/>
</div>

<div class='korisnik_info_desni'>
	<span class='korisnik_uid'>${korisnik.korisnicko_ime}</span>
	<span class='korisnik_datum_registracije'>Datum registracije: ${korisnik.datum_registracije}</span>
	<span class='korisnik_broj_poruka'>Broj oglasa: ${korisnik.broj_oglasa}</span>
</div>`;
}
		
	
Slika 20. - Funkcija za formatiranje informacija o korisniku.

Pomoćne funkcije za formatiranje oglasa

Funkcija formatiranjeOglas formatira pojedinačni oglas:

		
function formatiranjeOglas(naslov, tekst) {
	return `<div class='korisnik_oglas'>
	<div class='korisnik_oglas_naslov_oglasa'>${naslov}</div>
	<div class='korisnik_oglas_tekst'>${tekst}</div>
</div>`;
}
		
	
Slika 21. - Funkcije za formatiranje pojedinačnog oglasa.

Funkcija formatiranjeOglasi formatira spisak oglasa (preko funkcije formatiranjeOglas):

		
function formatiranjeOglasi(oglasi) {
	let s = "";

	oglasi.forEach(oglas => {
		s += formatiranjeOglas(oglas.naslov, oglas.tekst);
	});

	return s;
}
		
	
Slika 22. - Funkcija za formatiranje liste oglasa.

Pomoćne funkcije za formatiranje poruka

Funkcija formatiranjePoruka formatira pojedinačnu poruku:

		
function formatiranjePoruka(uid, vreme, tekst) {
	return `<div class='poruka'>
	<div class='poruka_posiljalac'>${uid} <span>(${vreme})</span></div>
	<div class='poruka_tekst'>${tekst}</div>
</div>`;
}
		
	
Slika 23. - Funkcija za formatiranje pojedinačne poruke.

Funkcija formatiranjePoruke (množina), formatira listu poruka:

		
function formatiranjePoruke(poruke) {
	let s = "";

	poruke.forEach(poruka => {
		s += formatiranjePoruka(poruka.korisnik, poruka.vreme, poruka.tekst);
	});

	return s;	
}
		
	
Slika 24. - Funkcija za formatiranje liste poruka.

Pomoćne funkcije za formatiranje spiska prijatelja

Funkcija formatiranjeKorisnikPrijatelj formatira podatke o pojedinačnom online prijatelju:

		
function formatiranjeKorisnikPrijatelj(uid, avatar) {
	return `<div class='korisnik_prijatelj_info'>
	<img class='korisnik_prijatelj_avatar' src='/slike/${avatar}'/>
	<span class='korisnik_prijatelj_uid'>${uid}</span>
</div>`;
}
		
	
Slika 25. - Funkcija za formatiranje podataka o pojedinačnom online prijatelju.

Funkcija formatiranjeKorisnikPrijatelji (množina), formatira listu online prijatelja:

		
function formatiranjeKorisnikPrijatelji(prijatelji) {
	let s = "";

	prijatelji.forEach(prijatelj => {
		s += formatiranjeKorisnikPrijatelj(prijatelj.korisnicko_ime, prijatelj.avatar);
	});

	return s;	
}
		
	
Slika 26. - Funkcija za formatiranje spiska 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):

		
// 1:
async function proveraKorisnikInfo(uid, lozinka) {
	uklanjanjeFormeZaPrijavu();
	// 2:
	return new Promise((resolve, reject) => {
		// 3:
		setTimeout(() => {
			fetch(URL_KORISNIK)
				.then(rez => rez.json())
				.then(korisnik => {
					if (uid == korisnik.korisnicko_ime &&
					   lozinka == korisnik.lozinka) {
						resolve(korisnik);	
					}
					else {
						reject("KORISNICKO IME I LOZINKA SE NE POKLAPAJU!");
					}
				})
				.catch(greska => reject("INFORMACIJE O KORISNIKU NISU PRONAĐENE!"));
		}, INTERVAL_KORISNIK_INFO);
	});
}
		
	
Slika 27. - Promis preko koga se učitavaju informacije o korisniku.

Struktura je relativno 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:

		
fetch(URL_KORISNIK)
	.then(rez => rez.json())
	.then(korisnik => {
		if (uid == korisnik.korisnicko_ime &&
		   lozinka == korisnik.lozinka) {
			resolve(korisnik);	
		}
		else {
			reject("KORISNICKO IME I LOZINKA SE NE POKLAPAJU!");
		}
	})
	.catch(greska => reject("INFORMACIJE O KORISNIKU NISU PRONAĐENE!"));
		
	
Slika 28. - Srž prethodnog promisa - funkcija fetch.

.... 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 funkcija fetch 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 spoljnjem delu koda (kada metoda proveraKorisnikaInfo vrati korektan rezultat, preko poziva resolve(korisnik))

Sada možemo definisati i ostale promise (koji su jednostavniji, u tom smislu što nemaju dodatni uslov) ....

- Promis za proveru oglasa:

		
async function proveraOglasa() {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			fetch(URL_OGLASI)
				.then(rez    => rez.json())
				.then(oglasi => resolve(oglasi))
				.catch(greska => reject("OGLASI NISU PRONAĐENI"));
		}, INTERVAL_OGLASI);
	});
}
		
	
Slika 29. - Promis za učitavanje oglasa sa servera.

- Promis za proveru poruka:

		
async function proveraPoruka() {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			fetch(URL_PORUKE)
				.then(rez     => rez.json())
				.then(poruke  => resolve(poruke))
				.catch(greska => reject("PORUKE NISU PRONAĐENE"));
		}, INTERVAL_PORUKE);
	});
}
		
	
Slika 30. - Promis za učitavanje poruka sa servera.

- Promis za proveru podataka o online prijateljima:

		
async function proveraPrijatelja() {
	return new Promise((resolve, reject) => {
		setTimeout(() => {
			fetch(URL_PRIJATELJI)
				.then(rez        => rez.json())
				.then(prijatelji => resolve(prijatelji))
				.catch(greska    => reject("PRIJATELJI NISU PRONAĐENI"));
		}, INTERVAL_PRIJATELJI);
	});
}
		
	
Slika 31. - Promis za učitavanje informacija o 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 sledećih zasebnih funkcija.

- Funkcija za učitavanje panela sa informacijama o prijavljenom korisniku:

		
function ucitavanjeKorisnikInfo(korisnik) {
	let tekst = formatiranjeKorisnikInfo(korisnik);
	ispisHTMLPolja(polje_korisnik_info, tekst, 'load');
}
		
	
Slika 32. - Funkcija za prikaz podataka o prijavljenom korisniku (koji su prethodno učitani sa servera).

- Funkcija za učitavanje oglasa (koje je prijavljeni korisnik objavio):

		
function ucitavanjeOglasa(oglasi) {
	let tekst = formatiranjeOglasi(oglasi);
	ispisHTMLPolja(polje_oglasi, tekst, 'load');
}
		
	
Slika 33. - Funkcija za prikaz oglasa (koji su prethodno učitani sa servera).

- Funkcija za učitavanje poruka (tj. komentara na oglase):

		
function ucitavanjePoruka(poruke) {
	let tekst = formatiranjePoruke(poruke);
	ispisHTMLPolja(polje_poruke, tekst, 'load');
}
		
	
Slika 34. - Funkcija za prikaz poruka (koje su prethodno učitane sa servera).

- Funkcija za učitavanje liste prijatelja koji su trenutno online:

		
function ucitavanjePrijatelja(prijatelji) {
	let tekst = formatiranjeKorisnikPrijatelji(prijatelji);
	ispisHTMLPolja(polje_prijatelji, tekst, 'load');
}
		
	
Slika 35. - Funkcija za prikaz liste prijatelja (koja je prethodno učitana sa servera).

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) ....

		
async function prijava() {
	let uid     = polje_uid.value;
	let lozinka = polje_lozinka.value;

	try {
		let rezPrijava = await proveraKorisnikInfo(uid, lozinka)
		ucitavanjeKorisnikInfo(rezPrijava)
		let rezOglasi = await proveraOglasa()
		ucitavanjeOglasa(rezOglasi);
		let poruke = await proveraPoruka()
		ucitavanjePoruka(poruke)
		let prijatelji = await proveraPrijatelja()
		ucitavanjePrijatelja(prijatelji);
	}
	catch (greska) {
		console.error(greska);
	}
}
		
	
Slika 36. - Glavna funkcija za prijavu korisnika, implementirana preko async/await sintakse.

.... 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 ....

Autor članka Nikola Vukićević Za web portal codeblog.rs
Napomena: Tekstovi, slike, web aplikacije i svi ostali sadržaji na sajtu codeblog.rs (osim u slučajevima gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog pismenog odobrenja autora.
© 2020-2025. Sva prava zadržana.
Facebook LinkedIn Twitter Viber WhatsApp E-mail
početna > Članci > Uvod u Fetch API
codeBlog codeBlog
Sajt posvećen popularizaciji kulture i veštine programiranja.
Napomena: Tekstovi i slike na sajtu codeblog.rs (osim u slučajevima, gde je drugačije navedeno) predstavljaju intelektualnu svojinu autora sajta codeblog.rs i zabranjeno je njihovo korišćenje na drugim sajtovima i štampanim medijima, kao i bilo kakvo drugo korišćenje u komercijalne svrhe, bez eksplicitnog odobrenja autora.
© 2020-2025. Sva prava zadržana.
Facebook - logo
Instagram - logo
LinkedIn - logo
Twitter - logo
E-mail
Naslovna
   •
Uslovi korišćenja
   •
Obaveštenja
   •
FAQ
   •
Kontakt