Asynchronní programování pomocí modifikátoru Async a operátoru Await

Asynchronní programovací model úlohy (TAP) poskytuje abstrakci asynchronního kódu. Kód napíšete jako posloupnost příkazů, stejně jako vždy. Tento kód si můžete přečíst, jako by se každý příkaz dokončil před dalším zahájením. Kompilátor provádí mnoho transformací, protože některé z těchto příkazů mohou začít pracovat a vrátit Task , která představuje probíhající práci.

To je cíl této syntaxe: povolte kód, který čte jako posloupnost příkazů, ale provádí se v mnohem složitějším pořadí na základě přidělení externích zdrojů a dokončení úkolů. Je to analogické, jak lidé poskytují pokyny pro procesy, které zahrnují asynchronní úlohy. V tomto článku použijete příklad pokynů k tomu, abyste zjistili, jak async jsou klíčová slova a await klíčová slova jednodušší zdůvodnit kód, který obsahuje řadu asynchronních instrukcí. Napište pokyny, jako je následující seznam, abyste vysvětlili, jak si udělat snídani:

  1. Nalít šálek kávy.
  2. Zahřejte pánev, pak smažíte dvě vejce.
  3. Smažte tři řezy slaniny.
  4. Toast dva kousky chleba.
  5. Přidejte máslo a džem na toast.
  6. Nalít sklenici pomerančové šťávy.

Pokud máte zkušenosti s vařením, provedete tyto pokyny asynchronně. Začnete ohřát pánev pro vejce a pak začnete slaninu. Chleba byste dal do toustovače a pak zahajte vejce. V každém kroku procesu byste zahájili úkol a pak zopakujte pozornost k úkolům, které jsou připravené pro vaši pozornost.

Vaření snídaně je dobrým příkladem asynchronní práce, která není paralelně. Jedna osoba (nebo vlákno) může zpracovávat všechny tyto úlohy. Pokračování v analogii snídaně může jedna osoba provést snídani asynchronně spuštěním dalšího úkolu před dokončením prvního úkolu. Vaření postupuje bez ohledu na to, jestli ho někdo sleduje. Jakmile začnete ohřívat pánev pro vejce, můžete začít s smažením slaniny. Jakmile se slanina začne, můžete chleba dát do toustovače.

Pro paralelní algoritmus byste potřebovali více kuchařů (nebo vláken). Jedna by udělala vejce, jednu slaninu a tak dále. Každý z nich by se zaměřoval jenom na tento jeden úkol. Každý kuchař (nebo vlákno) by byl zablokovaný synchronně čekající na to, aby baňka byla připravena překlopit nebo toast na pop.

Teď zvažte stejné pokyny napsané jako příkazy jazyka C#:

using System;
using System.Threading.Tasks;

namespace AsyncBreakfast
{
    // These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose.
    internal class Bacon { }
    internal class Coffee { }
    internal class Egg { }
    internal class Juice { }
    internal class Toast { }

    class Program
    {
        static void Main(string[] args)
        {
            Coffee cup = PourCoffee();
            Console.WriteLine("coffee is ready");

            Egg eggs = FryEggs(2);
            Console.WriteLine("eggs are ready");

            Bacon bacon = FryBacon(3);
            Console.WriteLine("bacon is ready");

            Toast toast = ToastBread(2);
            ApplyButter(toast);
            ApplyJam(toast);
            Console.WriteLine("toast is ready");

            Juice oj = PourOJ();
            Console.WriteLine("oj is ready");
            Console.WriteLine("Breakfast is ready!");
        }

        private static Juice PourOJ()
        {
            Console.WriteLine("Pouring orange juice");
            return new Juice();
        }

        private static void ApplyJam(Toast toast) =>
            Console.WriteLine("Putting jam on the toast");

        private static void ApplyButter(Toast toast) =>
            Console.WriteLine("Putting butter on the toast");

        private static Toast ToastBread(int slices)
        {
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("Putting a slice of bread in the toaster");
            }
            Console.WriteLine("Start toasting...");
            Task.Delay(3000).Wait();
            Console.WriteLine("Remove toast from toaster");

            return new Toast();
        }

        private static Bacon FryBacon(int slices)
        {
            Console.WriteLine($"putting {slices} slices of bacon in the pan");
            Console.WriteLine("cooking first side of bacon...");
            Task.Delay(3000).Wait();
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("flipping a slice of bacon");
            }
            Console.WriteLine("cooking the second side of bacon...");
            Task.Delay(3000).Wait();
            Console.WriteLine("Put bacon on plate");

            return new Bacon();
        }

        private static Egg FryEggs(int howMany)
        {
            Console.WriteLine("Warming the egg pan...");
            Task.Delay(3000).Wait();
            Console.WriteLine($"cracking {howMany} eggs");
            Console.WriteLine("cooking the eggs ...");
            Task.Delay(3000).Wait();
            Console.WriteLine("Put eggs on plate");

            return new Egg();
        }

        private static Coffee PourCoffee()
        {
            Console.WriteLine("Pouring coffee");
            return new Coffee();
        }
    }
}

synchronous breakfast

Synchronně připravená snídaně trvala zhruba 30 minut, protože celkový součet je součet jednotlivých úkolů.

Počítače tyto pokyny interpretují stejným způsobem jako lidé. Počítač bude blokovat každý příkaz, dokud nebude práce dokončena, než přejde na další příkaz. To vytvoří nespokojenou snídani. Pozdější úkoly by se neskončily, dokud nebyly dokončeny dřívější úkoly. Vytvoření snídaně by trvalo mnohem déle a některé položky by se před obsluhou zchladly.

Pokud chcete, aby počítač provedl výše uvedené pokyny asynchronně, musíte napsat asynchronní kód.

Tyto obavy jsou důležité pro programy, které píšete dnes. Při psaní klientských programů chcete, aby uživatelské rozhraní reagovalo na uživatelský vstup. Vaše aplikace by se neměla při stahování dat z webu zobrazovat jako zablokovaný. Při psaní serverových programů nechcete blokovat vlákna. Tato vlákna můžou obsluhovat další požadavky. Použití synchronního kódu v případě, že existují asynchronní alternativy, snižuje vaše schopnost vertikálně navýšit kapacitu na více instancí. Platíte za ty blokované vlákna.

Úspěšné moderní aplikace vyžadují asynchronní kód. Bez podpory jazyka psaní asynchronního kódu vyžaduje zpětné volání, události dokončení nebo jiné prostředky, které zakrývají původní záměr kódu. Výhodou synchronního kódu je, že jeho podrobné akce usnadňují kontrolu a pochopení. Tradiční asynchronní modely vás přinutily zaměřit se na asynchronní povahu kódu, nikoli na základní akce kódu.

Neblokujte, nečekejte místo toho

Předchozí kód ukazuje špatný postup: vytvoření synchronního kódu pro provádění asynchronních operací. Jak je uvedeno, tento kód zablokuje vlákno, které ho spouští, z jakékoli jiné práce. Nebude přerušena, zatímco probíhá žádný z úkolů. Bylo by to, jako kdybyste se dívali na toustovač po vložení chleba. Přeskočíš nikoho, kdo s tebou mluví, dokud se toust nevypíná.

Začněme aktualizací tohoto kódu tak, aby vlákno neblokoval během spouštění úloh. Klíčové await slovo poskytuje neblokující způsob, jak spustit úlohu, a pak pokračovat v provádění po dokončení úkolu. Jednoduchá asynchronní verze kódu pro snídani by vypadala jako následující fragment kódu:

// These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose.
internal class Bacon { }
internal class Coffee { }
internal class Egg { }
internal class Juice { }
internal class Toast { }

static async Task Main(string[] args)
{
    Coffee cup = PourCoffee();
    Console.WriteLine("coffee is ready");

    Egg eggs = await FryEggsAsync(2);
    Console.WriteLine("eggs are ready");

    Bacon bacon = await FryBaconAsync(3);
    Console.WriteLine("bacon is ready");

    Toast toast = await ToastBreadAsync(2);
    ApplyButter(toast);
    ApplyJam(toast);
    Console.WriteLine("toast is ready");

    Juice oj = PourOJ();
    Console.WriteLine("oj is ready");
    Console.WriteLine("Breakfast is ready!");
}

Důležité

Celková uplynulá doba je přibližně stejná jako počáteční synchronní verze. Kód ještě musí využívat některé klíčové funkce asynchronního programování.

Tip

Těla metody FryEggsAsync, FryBaconAsynca ToastBreadAsync všechny byly aktualizovány tak, aby se vrátily Task<Egg>, Task<Bacon>a Task<Toast> v uvedeném pořadí. Metody se přejmenují z původní verze tak, aby zahrnovaly příponu Async. Jejich implementace se zobrazují jako součást konečné verze dále v tomto článku.

Poznámka

Metoda Main vrátí Task, navzdory tomu, že nemá return výraz – to je návrh. Další informace najdete v tématu Vyhodnocení asynchronní funkce vracející void.

Tento kód neblokuje, zatímco vejce nebo slanina jsou vaření. Tento kód ale nespustí žádné další úkoly. Pořád bys dal toast do toustovače a zíral na to, dokud to nevykryje. Ale alespoň byste odpověděli na někoho, kdo chtěl vaši pozornost. V restauraci, kde se nachází více objednávek, může kuchař začít další snídani, zatímco první je vaření.

Teď není vlákno pracující na snídani zablokované, zatímco čeká na žádný spuštěný úkol, který ještě nebyl dokončen. U některých aplikací je tato změna všechna potřebná. Aplikace grafického uživatelského rozhraní stále reaguje na uživatele s právě touto změnou. V tomto scénáři ale potřebujete více. Nechcete, aby se jednotlivé úlohy komponent spouštěly postupně. Před čekáním na dokončení předchozího úkolu je lepší spustit jednotlivé úlohy komponenty.

Souběžné spouštění úloh

V mnoha scénářích chcete okamžitě spustit několik nezávislých úkolů. Jakmile se jednotlivé úkoly dokončí, můžete pokračovat v další práci, která je připravená. V analogii snídaně, to je způsob, jak se snídaně dělá rychleji. Získáte také všechno, co se děje téměř ve stejnou dobu. Dostanete horkou snídani.

Typy System.Threading.Tasks.Task a související typy jsou třídy, které můžete použít k odůvodnění probíhajících úkolů. Díky tomu můžete psát kód, který se více podobá způsobu vytváření snídaně. Najednou byste začali vařit vejce, slaninu a toast. Vzhledem k tomu, že každá vyžaduje akci, měli byste na tento úkol zaměřit pozornost, postarat se o další akci a pak počkat na něco jiného, co vyžaduje vaši pozornost.

Spustíte úkol a přidržíte ho na Task objekt, který představuje práci. Každý úkol provedete await před prací s jeho výsledkem.

Pojďme tyto změny v kódu snídaně udělat. Prvním krokem je uložení úloh pro operace při jejich spuštění, nikoli čekání na ně:

Coffee cup = PourCoffee();
Console.WriteLine("Coffee is ready");

Task<Egg> eggsTask = FryEggsAsync(2);
Egg eggs = await eggsTask;
Console.WriteLine("Eggs are ready");

Task<Bacon> baconTask = FryBaconAsync(3);
Bacon bacon = await baconTask;
Console.WriteLine("Bacon is ready");

Task<Toast> toastTask = ToastBreadAsync(2);
Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("Toast is ready");

Juice oj = PourOJ();
Console.WriteLine("Oj is ready");
Console.WriteLine("Breakfast is ready!");

Dále můžete před podáváním snídaně přesunout await příkazy pro slaninu a vejce na konec metody:

Coffee cup = PourCoffee();
Console.WriteLine("Coffee is ready");

Task<Egg> eggsTask = FryEggsAsync(2);
Task<Bacon> baconTask = FryBaconAsync(3);
Task<Toast> toastTask = ToastBreadAsync(2);

Toast toast = await toastTask;
ApplyButter(toast);
ApplyJam(toast);
Console.WriteLine("Toast is ready");
Juice oj = PourOJ();
Console.WriteLine("Oj is ready");

Egg eggs = await eggsTask;
Console.WriteLine("Eggs are ready");
Bacon bacon = await baconTask;
Console.WriteLine("Bacon is ready");

Console.WriteLine("Breakfast is ready!");

asynchronous breakfast

Asynchronně připravená snídaně trvala zhruba 20 minut, je to proto, že některé úkoly běžely souběžně.

Předchozí kód funguje lépe. Spustíte všechny asynchronní úlohy najednou. Každý úkol čekáte jenom v případě, že potřebujete výsledky. Předchozí kód může být podobný kódu ve webové aplikaci, která provádí požadavky na různé mikroslužby a pak kombinuje výsledky do jedné stránky. Všechny žádosti provedete okamžitě, pak await všechny tyto úkoly a vytvoříte webovou stránku.

Složení s úkoly

Máte všechno připravené na snídani najednou s výjimkou toastu. Toast je složení asynchronní operace (toasting chleba) a synchronní operace (přidání máslo a džemu). Aktualizace tohoto kódu znázorňuje důležitý koncept:

Důležité

Složení asynchronní operace následované synchronní prací je asynchronní operace. Uvedli jsme jiný způsob, pokud je některá část operace asynchronní, je celá operace asynchronní.

Předchozí kód vám ukázal, že můžete použít Task nebo Task<TResult> objekty k ukládání spuštěných úloh. Každý úkol před await použitím výsledku. Dalším krokem je vytvoření metod, které představují kombinaci jiné práce. Před podáváním snídaně chcete počkat na úkol, který představuje toasting chleba před přidáním máslo a džemu. Můžete reprezentovat tuto práci s následujícím kódem:

static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
{
    var toast = await ToastBreadAsync(number);
    ApplyButter(toast);
    ApplyJam(toast);

    return toast;
}

Předchozí metoda má async v podpisu modifikátor. To signalizuje kompilátoru, že tato metoda obsahuje await příkaz, který obsahuje asynchronní operace. Tato metoda představuje úkol, který toasts chleba, pak přidá máslo a jam. Tato metoda vrátí Task<TResult> , která představuje složení těchto tří operací. Hlavní blok kódu se teď stane:

// These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose.
internal class Bacon { }
internal class Coffee { }
internal class Egg { }
internal class Juice { }
internal class Toast { }

static async Task Main(string[] args)
{
    Coffee cup = PourCoffee();
    Console.WriteLine("coffee is ready");

    var eggsTask = FryEggsAsync(2);
    var baconTask = FryBaconAsync(3);
    var toastTask = MakeToastWithButterAndJamAsync(2);

    var eggs = await eggsTask;
    Console.WriteLine("eggs are ready");

    var bacon = await baconTask;
    Console.WriteLine("bacon is ready");

    var toast = await toastTask;
    Console.WriteLine("toast is ready");

    Juice oj = PourOJ();
    Console.WriteLine("oj is ready");
    Console.WriteLine("Breakfast is ready!");
}

Předchozí změna znázorňuje důležitou techniku pro práci s asynchronním kódem. Úkoly vytváříte oddělením operací do nové metody, která vrací úkol. Můžete zvolit, kdy chcete tento úkol očekávat. Další úlohy můžete spustit souběžně.

Asynchronní výjimky

Do tohoto okamžiku jste implicitně předpokládali, že všechny tyto úkoly byly úspěšně dokončeny. Asynchronní metody vyvolává výjimky stejně jako jejich synchronní protějšky. Asynchronní podpora výjimek a zpracování chyb se obecně snaží dosáhnout stejných cílů jako asynchronní podpora: Měli byste napsat kód, který se čte jako řada synchronních příkazů. Úkoly vyvolají výjimky, když se jim nedaří úspěšně dokončit. Kód klienta může tyto výjimky zachytit, když je awaitedspuštěný úkol . Předpokládejme například, že toustovač zachytí oheň při vytváření toastu. Můžete si to simulovat úpravou ToastBreadAsync metody tak, aby odpovídala následujícímu kódu:

private static async Task<Toast> ToastBreadAsync(int slices)
{
    for (int slice = 0; slice < slices; slice++)
    {
        Console.WriteLine("Putting a slice of bread in the toaster");
    }
    Console.WriteLine("Start toasting...");
    await Task.Delay(2000);
    Console.WriteLine("Fire! Toast is ruined!");
    throw new InvalidOperationException("The toaster is on fire");
    await Task.Delay(1000);
    Console.WriteLine("Remove toast from toaster");

    return new Toast();
}

Poznámka

Při kompilaci předchozího kódu se zobrazí upozornění týkající se nedostupného kódu. To je záměrné, protože jakmile toustovač zachytí požár, operace nebudou normálně pokračovat.

Po provedení těchto změn spusťte aplikaci a výstup bude podobný následujícímu textu:

Pouring coffee
Coffee is ready
Warming the egg pan...
putting 3 slices of bacon in the pan
Cooking first side of bacon...
Putting a slice of bread in the toaster
Putting a slice of bread in the toaster
Start toasting...
Fire! Toast is ruined!
Flipping a slice of bacon
Flipping a slice of bacon
Flipping a slice of bacon
Cooking the second side of bacon...
Cracking 2 eggs
Cooking the eggs ...
Put bacon on plate
Put eggs on plate
Eggs are ready
Bacon is ready
Unhandled exception. System.InvalidOperationException: The toaster is on fire
   at AsyncBreakfast.Program.ToastBreadAsync(Int32 slices) in Program.cs:line 65
   at AsyncBreakfast.Program.MakeToastWithButterAndJamAsync(Int32 number) in Program.cs:line 36
   at AsyncBreakfast.Program.Main(String[] args) in Program.cs:line 24
   at AsyncBreakfast.Program.<Main>(String[] args)

Všimněte si, že mezi tím, kdy toustovač zachytí požár, a zjistíte výjimku, je dokončeno poměrně málo úkolů. Pokud úloha, která běží asynchronně, vyvolá výjimku, je tato úloha chybná. Objekt Task obsahuje výjimku vyvolánou ve Task.Exception vlastnosti. Chybné úkoly při čekané výjimce můžou vyvolat výjimku.

Existují dva důležité mechanismy, které je potřeba pochopit: jak je výjimka uložená v chybné úloze a jak se výjimka rozbalí a znovu se přehrá, když kód čeká na chybný úkol.

Když kód spuštěný asynchronně vyvolá výjimku, tato výjimka je uložena v souboru Task. Vlastnost Task.Exception je důvodem System.AggregateException , protože během asynchronní práce může být vyvolán více než jedna výjimka. Do kolekce se přidá AggregateException.InnerExceptions jakákoli výjimka, která se vyvolá. Pokud je tato Exception vlastnost null, vytvoří se nová AggregateException a vyvoláná výjimka je první položka v kolekci.

Nejběžnějším scénářem chybné úlohy je, že Exception vlastnost obsahuje přesně jednu výjimku. Když kóduje awaits chybný úkol, první výjimka v kolekci AggregateException.InnerExceptions se znovu zřetězí. Proto výstup z tohoto příkladu ukazuje místo InvalidOperationExceptionAggregateException. Extrahování první vnitřní výjimky dělá práci s asynchronními metodami co nejblíže práci se svými synchronními protějšky. Vlastnost v kódu můžete prozkoumat Exception , když váš scénář může vygenerovat více výjimek.

Než začnete, zakomentujte tyto dva řádky ve vaší ToastBreadAsync metodě. Nechcete zahájit další požár:

Console.WriteLine("Fire! Toast is ruined!");
throw new InvalidOperationException("The toaster is on fire");

Efektivní čeká na úkoly

Řadu await příkazů na konci předchozího kódu lze vylepšit pomocí metod Task třídy. Jednou z těchto rozhraní API je WhenAll, která vrátí Task dokončení všech úkolů v seznamu argumentů, jak je znázorněno v následujícím kódu:

await Task.WhenAll(eggsTask, baconTask, toastTask);
Console.WriteLine("Eggs are ready");
Console.WriteLine("Bacon is ready");
Console.WriteLine("Toast is ready");
Console.WriteLine("Breakfast is ready!");

Další možností je použít WhenAny, která vrátí Task<Task> hodnotu, která se dokončí, když se některý z jeho argumentů dokončí. Můžete očekávat vrácený úkol a vědět, že už je dokončený. Následující kód ukazuje, jak můžete použít WhenAny k tomu, abyste čekali na dokončení prvního úkolu a pak mohli zpracovat jeho výsledek. Po zpracování výsledku z dokončeného úkolu odeberete dokončený úkol ze seznamu úkolů předaných do WhenAny.

var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
while (breakfastTasks.Count > 0)
{
    Task finishedTask = await Task.WhenAny(breakfastTasks);
    if (finishedTask == eggsTask)
    {
        Console.WriteLine("Eggs are ready");
    }
    else if (finishedTask == baconTask)
    {
        Console.WriteLine("Bacon is ready");
    }
    else if (finishedTask == toastTask)
    {
        Console.WriteLine("Toast is ready");
    }
    breakfastTasks.Remove(finishedTask);
}

Po všech těchto změnách vypadá konečná verze kódu takto:

using System;
using System.Collections.Generic;
using System.Threading.Tasks;

namespace AsyncBreakfast
{
    // These classes are intentionally empty for the purpose of this example. They are simply marker classes for the purpose of demonstration, contain no properties, and serve no other purpose.
    internal class Bacon { }
    internal class Coffee { }
    internal class Egg { }
    internal class Juice { }
    internal class Toast { }

    class Program
    {
        static async Task Main(string[] args)
        {
            Coffee cup = PourCoffee();
            Console.WriteLine("coffee is ready");

            var eggsTask = FryEggsAsync(2);
            var baconTask = FryBaconAsync(3);
            var toastTask = MakeToastWithButterAndJamAsync(2);

            var breakfastTasks = new List<Task> { eggsTask, baconTask, toastTask };
            while (breakfastTasks.Count > 0)
            {
                Task finishedTask = await Task.WhenAny(breakfastTasks);
                if (finishedTask == eggsTask)
                {
                    Console.WriteLine("eggs are ready");
                }
                else if (finishedTask == baconTask)
                {
                    Console.WriteLine("bacon is ready");
                }
                else if (finishedTask == toastTask)
                {
                    Console.WriteLine("toast is ready");
                }
                breakfastTasks.Remove(finishedTask);
            }

            Juice oj = PourOJ();
            Console.WriteLine("oj is ready");
            Console.WriteLine("Breakfast is ready!");
        }

        static async Task<Toast> MakeToastWithButterAndJamAsync(int number)
        {
            var toast = await ToastBreadAsync(number);
            ApplyButter(toast);
            ApplyJam(toast);

            return toast;
        }

        private static Juice PourOJ()
        {
            Console.WriteLine("Pouring orange juice");
            return new Juice();
        }

        private static void ApplyJam(Toast toast) =>
            Console.WriteLine("Putting jam on the toast");

        private static void ApplyButter(Toast toast) =>
            Console.WriteLine("Putting butter on the toast");

        private static async Task<Toast> ToastBreadAsync(int slices)
        {
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("Putting a slice of bread in the toaster");
            }
            Console.WriteLine("Start toasting...");
            await Task.Delay(3000);
            Console.WriteLine("Remove toast from toaster");

            return new Toast();
        }

        private static async Task<Bacon> FryBaconAsync(int slices)
        {
            Console.WriteLine($"putting {slices} slices of bacon in the pan");
            Console.WriteLine("cooking first side of bacon...");
            await Task.Delay(3000);
            for (int slice = 0; slice < slices; slice++)
            {
                Console.WriteLine("flipping a slice of bacon");
            }
            Console.WriteLine("cooking the second side of bacon...");
            await Task.Delay(3000);
            Console.WriteLine("Put bacon on plate");

            return new Bacon();
        }

        private static async Task<Egg> FryEggsAsync(int howMany)
        {
            Console.WriteLine("Warming the egg pan...");
            await Task.Delay(3000);
            Console.WriteLine($"cracking {howMany} eggs");
            Console.WriteLine("cooking the eggs ...");
            await Task.Delay(3000);
            Console.WriteLine("Put eggs on plate");

            return new Egg();
        }

        private static Coffee PourCoffee()
        {
            Console.WriteLine("Pouring coffee");
            return new Coffee();
        }
    }
}

when any async breakfast

Konečná verze asynchronně připravené snídaně trvala přibližně 15 minut, protože některé úlohy běžely souběžně a kód monitoroval více úkolů najednou a provedl akci jenom v případě potřeby.

Tento konečný kód je asynchronní. Přesněji odpovídá tomu, jak by člověk uvařil snídani. Porovnejte předchozí kód s prvním vzorovým kódem v tomto článku. Základní akce jsou stále jasné ze čtení kódu. Tento kód si můžete přečíst stejným způsobem, jako byste si přečetli tyto pokyny k vytvoření snídaně na začátku tohoto článku. Jazykové funkce pro async a await poskytují překlad každé osobě, aby postupovaly podle těchto psaných pokynů: zahajte úkoly, jak můžete a neblokujte čekání na dokončení úkolů.

Další kroky