07.12.2022, Vladimír Klaus, navštíveno 1104x

JavaScript

O asynchronním zpracování v různých jazycích toho bylo napsáno již hodně, ale bohužel jde často jen o velmi jednoduché příklady. Ty sice fungují, ale ve chvíli, kdy se přesunete do nějaké reálné aplikace, může dojít k tomu, že to fungovat přestane. A přitom stačí málo, dávat si pozor, vhodně pojmenovat funkce a především dobře porozumět principům.

Jak správně používat async, await, promise, then v JavaScriptu resp. TypeScriptu

Zkusíme si připravit malou "aplikaci", která spustí asynchronně nějakou funkci, a po celou dobu bude logovat (pomocí číselné posloupnosti), kde se právě nachází, tedy co se vykonává.

Ve stránce spouštíme pouze funkci Hlavni(), která loguje začátek, pak spouští funkci Test() a nakonec loguje konec. Funkce Test je asynchronní, loguje začátek, spouští paralelně další funkci ParalelniFunkce(), přičemž čeká na výsledek a loguje konec. Paralelní funkce loguje začátek, provádí "náročný" výpočet a loguje konec. A samozřejmě požadujeme, aby všechno proběhlo ve správném pořadí.

function Hlavni() {
    console.log('00 - BEGIN');
    Test()
    console.log('99 - END')
}

async function Test() {
    console.log('10');
    await ParalelniFunkce()
    console.log('50');
}

function ParalelniFunkce() {
    console.log('22');
    let a: number = 0
    for (var i = 0; i < 1000; i++) {
        a = a + i
    }
    console.log('33');
}

Hlavni()

Asynchronní funkci máme správně označenou async, uvnitř se paralelní funkce volá s await - abychom čekali na její dokončení, takže se všechno jeví, jako zcela korektní. Když se však podíváte do konzole, zjistíte, že máte problém. Aplikace skončila dříve (99) než doběhlo čekání na paralelní zpracování (50).

Jak správně používat async, await, promise, then v JavaScriptu resp. TypeScriptu, obr. 1

A to je ten hlavní důvod, proč vznikl tento článek - stále musíte mít na mysli to, že jakmile začnete zanořovat paralelní funkce, tak musíte všude čekat na výsledek. To je právě problém většiny tutoriálů, kdy ukáží jen jednoduché použití. V našem případě by vůbec neexistovala funkce Hlavni() a rovnou by se volal Test(), což by pochopitelně prošlo bez problémů.

Takže co s tím. Nabízí se 2 varianty, které fungují stejně, pouze se liší syntaxe zápisu a pro někoho i přehlednost.

Varianta č. 1 s pomocí .then()

Ještě nebylo zmíněno, že jakákoliv async funkce vrací Promise. Tedy jakýsi příslib, který buď bude nebo nebude splněn. A my můžeme čekat na jeho splnění tím, že doplníme za volání funkce rovnou .then(), tedy, co se má stát potom, co funkce doběhne.

function Hlavni() {
    console.log('00 - BEGIN');
    Test().then(() => {
        console.log('99 - END')
    })
}

Výsledek je nyní již zcela korektní a dává smysl.

Jak správně používat async, await, promise, then v JavaScriptu resp. TypeScriptu, obr. 2

Varianta č. 2 s pomocí dalšího async/await

Druhou variantou je doplnit await čekání i do hlavní funkce. A to znamená i hlavní funkci označit pomocí async.

async function Hlavni() {
    console.log('00 - BEGIN');
    await Test()
    console.log('99 - END')
}

Pak je zde ještě další část, kterou nesmíme podcenit, a tou je hlídání chyb. Jinak řečeno - slib nemusí být splněn. Proto si do asynchronní funkce přidáme hlídání přes try/catch.

Jak správně používat async, await, promise, then v JavaScriptu resp. TypeScriptu, obr. 3

Abych se vyhnul problémům způsobeným špatným voláním paralelních funkcí, řeším to tak, že async funkce nazývám asyncXXXXX(). Pak mi i s odstupem času dojde, že si musím dávat pozor na jejich užívání.

Výsledný kód tedy vypadá takto.

function Hlavni() {
    console.log('00 - BEGIN');
    asyncTest().then(() => {
        console.log('99 - END')
    })
}

async function asyncTest() {
    console.log('10');
    try {
        await ParalelniFunkce()
        console.log('50');

    } catch (e) {
        console.error('55 ' + e)
    }
}

function ParalelniFunkce() {
    console.log('22');
    let a: number = 0
    for (var i = 0; i < 1000; i++) {
        a = a + i
    }
    console.log('33');
    throw new Error("Testovací chyba")
}

Hlavni()

Zdroje: