JavaScript Async/Await Vysvětleno za 10 minut

Po nejdelší dobu museli vývojáři JavaScriptu při práci s asynchronním kódem spoléhat na zpětná volání. Výsledkem je, že mnoho z nás zažilo peklo zpětného volání a hrůzu, kterou člověk prožívá, když čelí funkcím vypadajícím takto.

Naštěstí pak (nebo bychom měli říct .then() ) přišly Sliby. Nabízely mnohem organizovanější alternativu zpětných volání a většina komunity rychle přešla k jejich používání.

Nyní, s nejnovějším přírůstkem Async/Await, bude psaní kódu JavaScript ještě lepší!

Co je Async/Await?

Async/Await je dlouho očekávaná funkce JavaScriptu, díky které je práce s asynchronními funkcemi mnohem příjemnější a snadněji pochopitelná. Je postaven na Promises a je kompatibilní se všemi existujícími API založenými na Promise.

Název pochází z async a await – dvě klíčová slova, která nám pomohou vyčistit náš asynchronní kód:

Asynchronní – deklaruje asynchronní funkci (async function someName(){...} ).

  • Automaticky transformuje běžnou funkci na Promise.
  • Při volání asynchronních funkcí se vyřeší vše, co je vráceno v jejich těle.
  • Asynchronní funkce umožňují použití await .

Await - pozastaví provádění asynchronních funkcí. (var result = await someAsyncCall(); ).

  • Když je umístěn před hovorem Promise, await vynutí zbytek kódu, aby počkal, dokud slib neskončí a vrátí výsledek.
  • Await funguje pouze s Promises, nefunguje se zpětnými voláními.
  • Await lze použít pouze uvnitř async funkce.

Zde je jednoduchý příklad, který snad vše objasní:

Řekněme, že chceme získat nějaký soubor JSON z našeho serveru. Napíšeme funkci, která využívá knihovnu axios a odešle HTTP GET požadavek na https://tutorialzine.com/misc/files/example.json. Musíme počkat, až server odpoví, takže tento požadavek HTTP bude přirozeně asynchronní.

Níže můžeme vidět stejnou funkci implementovanou dvakrát. Nejprve pomocí Promises, poté podruhé pomocí Async/Await.

// Promise approach

function getJSON(){

    // To make the function blocking we manually create a Promise.
    return new Promise( function(resolve) {
        axios.get('https://tutorialzine.com/misc/files/example.json')
            .then( function(json) {

                // The data from the request is available in a .then block
                // We return the result using resolve.
                resolve(json);
            });
    });

}

// Async/Await approach

// The async keyword will automatically create a new Promise and return it.
async function getJSONAsync(){

    // The await keyword saves us from having to write a .then() block.
    let json = await axios.get('https://tutorialzine.com/misc/files/example.json');

    // The result of the GET request is available in the json variable.
    // We return it just like in a regular synchronous function.
    return json;
}

Je celkem jasné, že verze kódu Async/Await je mnohem kratší a snáze čitelná. Kromě použité syntaxe jsou obě funkce zcela totožné – obě vracejí Promises a řeší s odpovědí JSON z axios. Naši asynchronní funkci můžeme nazvat takto:

getJSONAsync().then( function(result) {
    // Do something with result.
});

Takže jsou sliby Async/Await zastaralé?

Vůbec ne. Při práci s Async/Await stále používáme Promises pod kapotou. Dobré porozumění Promises vám skutečně pomůže z dlouhodobého hlediska a je vysoce doporučeno.

Existují dokonce případy použití, kdy to Async/Await nesežene a my se musíme vrátit k Promises pro pomoc. Jedním z takových scénářů je situace, kdy potřebujeme provést více nezávislých asynchronních volání a počkat, až všechna skončí.

Pokud to zkusíme provést async a čekáme, stane se následující:

async function getABC() {
  let A = await getValueA(); // getValueA takes 2 second to finish
  let B = await getValueB(); // getValueB takes 4 second to finish
  let C = await getValueC(); // getValueC takes 3 second to finish

  return A*B*C;
}

Každý čekající hovor bude čekat na předchozí a vrátí výsledek. Vzhledem k tomu, že provádíme jeden hovor najednou, celá funkce bude trvat 9 sekund od začátku do konce (2+4+3).

Toto není optimální řešení, protože tři proměnné A , B a C nejsou na sobě závislí. Jinými slovy, nepotřebujeme znát hodnotu A než dostaneme B . Můžeme je získat ve stejnou dobu a zkrátit si pár sekund čekání.

Chcete-li odeslat všechny požadavky současně, Promise.all() je požadováno. To zajistí, že před pokračováním budeme mít stále všechny výsledky, ale asynchronní volání se budou spouštět paralelně, nikoli jedno po druhém.

async function getABC() {
  // Promise.all() allows us to send all requests at the same time. 
  let results = await Promise.all([ getValueA, getValueB, getValueC ]); 

  return results.reduce((total,value) => total * value);
}

Funkce tak zabere mnohem méně času. getValueA a getValueC hovory budou již ukončeny v čase getValueB končí. Místo součtu časů efektivně zkrátíme provedení na čas nejpomalejšího požadavku (getValueB - 4 sekundy).

Zpracování chyb v režimu Async/Await

Další skvělá věc na Async/Await je, že nám umožňuje zachytit jakékoli neočekávané chyby ve starém dobrém bloku try/catch. Potřebujeme jen zabalit naše await volá takto:

async function doSomethingAsync(){
    try {
        // This async call may fail.
        let result = await someAsyncCall();
    }
    catch(error) {
        // If it does we will catch the error here.
    }  
}

Klauzule catch zpracuje chyby vyvolané očekávanými asynchronními voláními nebo jakýmkoli jiným chybným kódem, který jsme mohli zapsat do bloku try.

Pokud to situace vyžaduje, můžeme také zachytit chyby při provádění asynchronní funkce. Protože všechny asynchronní funkce vracejí Promises, můžeme jednoduše zahrnout .catch() při jejich volání.

// Async function without a try/catch block.
async function doSomethingAsync(){
    // This async call may fail.
    let result = await someAsyncCall();
    return result;  
}

// We catch the error upon calling the function.
doSomethingAsync().
    .then(successHandler)
    .catch(errorHandler);

Je důležité vybrat si, jakou metodu řešení chyb preferujete, a toho se držet. Současné použití try/catch a .catch() pravděpodobně povede k problémům.

Podpora prohlížeče

Async/Await je již k dispozici ve většině hlavních prohlížečů. To nezahrnuje pouze IE11 – všichni ostatní dodavatelé rozpoznají váš asynchronní/čekací kód bez potřeby externích knihoven.

Vývojáři uzlů mohou také využívat vylepšený asynchronní tok, pokud jsou na Node 8 nebo novějším. Koncem tohoto roku by se měl stát LTS.

Pokud vás tato kompatibilita neuspokojuje, existuje také několik transpilátorů JS, jako je Babel a TypeScript, a knihovna Node.js asyncawait, které nabízejí své vlastní verze této funkce pro různé platformy.

Závěr

S přidáním Async/Await udělal jazyk JavaScript obrovský skok vpřed, pokud jde o čitelnost kódu a snadné použití. Schopnost psát asynchronní kód, který se podobá běžným synchronním funkcím, ocení jak začátečníci v JavaScriptu, tak zkušení kodéři.

  • Asynchronní na MDN
  • Čekejte na MDN
  • Async/Await:The Hero JavaScript Deserved
  • Odkud se vzal Async/Await a proč jej používat?