Lekce 3 - Promises a asynchronní funkce
V minulé lekci, Struktura projektu a životní cyklus aplikace, jsme si rozebrali strukturu React Native projektu a vysvětlili, co se v aplikaci děje od jejího spuštění až po ukončení.
V dnešním tutoriálu vývoje multiplatformních mobilních aplikací v React Native si popíšeme promises a asynchronní funkce a ukážeme si příklady jejich využití.
Promises v JavaScriptu
Promises (budeme se držet anglické verze) jsou v moderním JavaScriptu základem asynchronního programování. Promise je objekt představující budoucí dokončení či selhání asynchronní operace. V okamžiku, kdy je promise vrácen volajícímu, často ještě není operace dokončena. Promise ale poskytuje metody pro zpracování jak případného úspěchu, tak i neúspěchu operace. Promise je vždy v jednom ze tří stavů:
- pending - defaultní stav, není ani vyřešený, ani odmítnutý,
- fulfilled - promise je vyřešený, tzn. operace byla úspěšně dokončena,
- rejected - promise je odmítnutý, tzn. operace skončila neúspěchem.
Metoda then()
Metoda then()
instance promise přijímá až dva argumenty.
Jsou jimi callback funkce pro případ úspěchu a neúspěchu. Tato metoda
vrací nový promise, což umožňuje
řetězit další then()
metody.
Syntaxe promise
Pojďme se podívat, jak takový promise obvykle vypadá:
const mujPrvniPromise = new Promise((resolve, reject) => { setTimeout(() => { resolve("Úspěch!"); }, 1000); }); mujPrvniPromise.then((zprava) => { console.log(`Hurá! ${zprava}`); });
Ukázka kódu vytváří nový promise objekt s názvem
mujPrvniPromise
. Tento objekt má jako parametry funkce
resolve()
a reject()
:
- funkce
resolve()
se volá, když operace, kterou promise zastupuje, proběhne úspěšně, - funkce
reject()
se volá v případě, kdy dojde k nějaké chybě během operace.
Uvnitř těla promise je pro simulaci asynchronní operace použitá funkce
setTimeout()
. Po uplynutí sekundy (1000
milisekund)
je volána funkce resolve()
s řetězcem "Úspěch!"
.
To značí, že asynchronní operace byla dokončena úspěšně.
Pokud bychom chtěli simulovat skutečnou asynchronní operaci,
místo setTimeout()
bychom použili např. funkci
fetch()
, která je v JavaScriptu běžně používaná pro
asynchronní síťové požadavky.
Metoda then()
objektu promise definuje, co se má stát poté,
co je promise úspěšně vyřešen (tedy když je volána funkce
resolve()
). V našem případě se vypíše do konzole zpráva
"Hurá! Úspěch!"
. No a zprava
je parametr funkce,
který obsahuje hodnotu předanou metodě resolve()
- v našem
příkladu je to řetězec "Úspěch!"
.
Funkce reject()
Když pracujeme s promises, je důležité řádně ošetřit potenciální
chyby. Už víme, že funkce reject()
slouží k označení promise
jako neúspěšného, tzn. že operace skončila chybou. Tato funkce, stejně
jako resolve()
, přijímá argument popisující důvod selhání.
Podívejme se na upravenou ukázku předchozího kódu:
const mujPrvniPromise = new Promise((resolve, reject) => { setTimeout(() => { const uspesnaOperace = false; if (uspesnaOperace) { resolve("Úspěch!"); } else { reject("Něco se pokazilo!"); } }, 1000); }); mujPrvniPromise .then((zprava) => { console.log(Hurá! ${ zprava }); }) .catch((chyba) => { console.log(Bohužel: ${ chyba }); });
V příkladu výše jsme přidali jednoduchou logiku, která rozhoduje, zda
operace proběhla úspěšně nebo ne. Pokud se operace nepovede, je volána
funkce reject()
s chybovou zprávou. Pro zpracování těchto chyb
v promises používáme metodu catch()
, která je navázána po
metodě then()
. V našem příkladu, pokud operace selže, se
vypíše do konzole zpráva Bohužel: Něco se pokazilo!
.
Asynchronní funkce
Deklarace asynchronní funkce pomocí klíčového slova async
vytvoří instanci AsyncFunction
. Pokaždé, když je asynchronní
funkce zavolána, vrátí nový promise, který je buď vyřešen (při
úspěchu, anglicky resolved), v takovém případě vrátí hodnotu
asynchronní funkce, nebo je odmítnut (při chybě, anglicky rejected) s
výjimkou nezachycenou v rámci asynchronní funkce.
Asynchronní funkce mohou obsahovat libovolný počet výrazů s klíčovým
slovem await
. Klíčové slovo await
smí být
použito pouze uvnitř asynchronní funkce. Výraz s klíčovým slovem
await
pozastaví vykonávání kódu v asynchronní funkci, dokud
není promise vyřešen nebo odmítnut, čímž může dávat dojem synchronní
operace. Pokud je promise vyřešen, jeho vrácená hodnota je přiřazena k
výrazu s klíčovým slovem await
. Pokud je promise odmítnut a
výraz s klíčovým slovem await
není v rámci
try
/catch
bloku, dojde k vyhození výjimky.
Asynchronní funkce také umožňují používat try
/
catch
bloky kolem asynchronního kódu pro zachycení těchto
výjimek.
Syntaxe asynchronních funkcí
Nyní se podíváme na jednoduchý příklad asynchronní funkce:
async function fetchData() { try { const odpoved = await fetch('https://api.priklad.com/data'); const data = await odpoved.json(); return data; } catch (e) { console.log('Vyskytla se chyba:', e); throw e; } }
V kódu výše definujeme asynchronní funkci fetchData()
. Před
definicí funkce je klíčové slovo async
, což umožňuje v
rámci této funkce používat klíčové slovo await
.
Tato funkce se skládá ze dvou hlavních částí
Blok try
- na třetím řádku používáme funkci
fetch()
k získání odpovědi od specifikovaného API. Klíčové slovoawait
znamená, že kód bude pozastaven, dokud se nevrátí odpověď (nebo v případě chyby výjimka), - následně používáme na této odpovědi metodu
json()
. Je důležité poznamenat, žejson()
je skutečně asynchronní metoda, což znamená, že vrací promise. K získání výsledných dat ve formátu JSON z tohoto promise opět používáme klíčové slovoawait
.
Blok catch
- pokud v jakékoli části
try
bloku dojde k chybě (např. problém s komunikací s API nebo problém s parsováním JSONu), kód vstoupí docatch
bloku, - v něm vypisujeme chybu do konzole pomocí metody
console.log()
a poté ji vyhazujeme pomocí klíčového slovathrow
, což umožňuje dalšímu kódu nebo volající funkci na tuto chybu reagovat.
Klíčové slovo await
smíme použít pouze v
asynchronní funkci, jinak kód vyústí v SyntaxError
.
Příklady využití asynchronních funkcí
Jakmile obdržíme odpověď pomocí fetch()
requestu, musíme
zavolat další funkci, abychom získali vyžadovaná data. V tomto příkladu
chceme získat data ve formátu JSON, proto voláme metodu json()
.
Důležité je si uvědomit, že i když json()
zpracovává
odpověď naší žádosti, je to stále asynchronní funkce.
To znamená, že pro získání dat a jejich konverzi na formát JSON
potřebujeme postupně zavolat dvě asynchronní funkce:
const fetchPromise = fetch( "https://api.com/products.json", ); fetchPromise.then((response) => { const jsonPromise = response.json(); jsonPromise.then((data) => { console.log(data); }); });
Toto řešení je sice funkční, není ovšem optimální. Elegantní
vlastností promisů je, jak jsme si již říkali, že sama then()
metoda vrací promise, který bude vyřešen funkcí doplněnou
jako parametr. To znamená, že tento kód můžeme (a měli bychom 🙂)
přepsat do následující podoby:
const fetchPromise = fetch( "https://api.com/products.json", ); fetchPromise .then((response) => response.json()) .then((data) => { console.log(data); });
Místo volání druhé then()
metody uvnitř první
then()
metody jsme vrátili promise, vrácený metodou
json()
a zavolali druhou then()
metodu na této
vrácené hodnotě. Tímto se vyhneme stále zvyšujícím se úrovním
odsazení při volání více asynchronních funkcí.
Nyní si pojďme ukázat ještě jeden příklad asynchronní funkce:
async function jednoduchaFunkce() { try { await ziskejDataZeServeru() .then(vysledek => console.log('Data ze serveru:', vysledek)); } catch (error) { console.log('Vyskytla se chyba:', error); } } function ziskejDataZeServeru() { return new Promise(resolve => { setTimeout(() => { // simulace serveru resolve('Data získána, hurá!'); }, 1000); }); } jednoduchaFunkce();
V tomto příkladu jednoduchaFunkce()
čeká na data ze serveru.
Poté jsme použili then()
metodu, abychom vypsali výsledek
operace. Funkce ziskejDataZeServeru()
vrací promise, který
simuluje server a je vyřešen po uplynutí jedné vteřiny. Funkce
resolve()
vrací řetězec, který je následně vypsán do konzole
v první funkci.
V příští lekci, První multiplatformní aplikace, opustíme teorii a naprogramujeme si jednoduchou aplikaci, která bude reagovat na vstup od uživatele a zobrazí nám dialogové okno s pozdravem.