Hledáme nové posily do ITnetwork týmu. Podívej se na volné pozice a přidej se do nejagilnější firmy na trhu - Více informací.

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é slovo await 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, že json() 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é slovo await.

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í do catch bloku,
  • v něm vypisujeme chybu do konzole pomocí metody console.log() a poté ji vyhazujeme pomocí klíčového slova throw, 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.


 

Předchozí článek
Struktura projektu a životní cyklus aplikace
Všechny články v sekci
React Native
Přeskočit článek
(nedoporučujeme)
První multiplatformní aplikace
Článek pro vás napsal Štěpán Kraus
Avatar
Uživatelské hodnocení:
11 hlasů
Aktivity