krouzek:async_await

Doprovodný zdrojový kód: async_await_html.zip

Intro

Většina procedurálních jazyků primárně používá blokující funkce

  • čekání na vstup (klávesa, myš) uživatele
  • načítání dat ze souboru
  • síťové dotazy

Například v pythonu:

jmeno = input('Kdo jsi?\n') # zde se čeká dokud uživatel nenapíše jméno
# teď můžu pracovat s hodnotou proměnné jméno, mám ji načtenou
print(f"Ahoj {jmeno}")

K zajištění asynchronicity (například program něco počítá, ale uživatel dál něco nastavuje v okně programu) se používají o něco (zápisem) komplikovanější postupy, které vnitřně používají vlákna (thready).

V javascriptu je to opačně. Přirozené použití vede na asynchronitu. Primárně se používají události (events); na něco jsou skvělé, na jiné věci méně.

Uvnitř funguje jednovláknový „event loop“ - ucelené kusy výpočtu se naplánují a postupně vykonají.

Eventy

  • Uživatelské eventy:
    • Klik na tlačítko
    • Klik na nějaký prvek stránky (případně mousedown, mouseup)
    • Přejetí nad prvkem stránky (mouseover)
    • Livovolný pohyb myši (mousemove)
    • Změna velikosti okna (např. maximalizace)
    • keydown, keyup (nebo keypress)
    • kliknutí do/z okna (focus, blur)
  • Další:
    • Stránka byla načtena (load)
    • Datový zdroj byl stažen (např. json s mailem) - původní AJAX, příklad použití

Callbacky

  • setTimeout
  • setInterval
  • rozhraní různých knihoven

Často je výsledkem callback hell


Řešení (2012 ale dlouho trvalo než probublalo do prohlížečů)

Promises

        let myPromise = new Promise(function(myResolve, myReject) {
            // "Producing Code" (May take some time)
 
            myResolve(); // when successful
            myReject();  // when error
        });
 
        // "Consuming Code" (Must wait for a fulfilled Promise)
        myPromise.then(
          function(value) { /* code if successful */ },
          function(error) { /* code if some error */ }
        );

Zdánlivě jen další příklad callbacku, ale `.then()` lze řetězit

new Promise(function(resolve, reject) {
  setTimeout(() => resolve(1), 1000); // (*)
}).then(function(result) { // (**)
  alert(result); // 1
  return result * 2;
}).then(function(result) { // (***)
  alert(result); // 2
  return result * 2;
}).then(function(result) {
  alert(result); // 4
  return result * 2;
});

a existující callback funkce lze převést na promisy

function sleep(ms) {
    return new Promise(resolve => setTimeout(resolve, ms));
}
  const nactiStranku = new Promise(function(myResolve, myReject) {
    const xhttp = new XMLHttpRequest();
    xhttp.onload = function() {
      myResolve(xhttp)
    }
    xhttp.onerror = function(){
      myReject()
    }
    // Send a request
    xhttp.open("GET", "ajax_info.txt");
    xhttp.send();
  });

Async/Await

Proč vůbec stále psát then() ? Pojďmě udělat speciální kategorii funkcí, které označíme async

  • Interně vrací Promise.
  • Při použití nového klíčového slova await se na daném místě „zastaví“ (ovšem s uvolněním event loopu) a počká se na splnění promise (např. dotazu na server)
  • Neúspěch (reject) Promise ⇒ vyplivne výjimku (throw an exception) - lze ji ošetřit pomocí try-catch což je dnes standardní postup většiny jazyků

Příklad výsledného kódu:

 const pokemoni = await nactiSeznamPokemonu()
 const grafika = await nactiObrazkyPokemonu(pokemoni)
 await pockejNaMezeru()
 zacniHru()
 /* ... */

Jediný implementační problém: await může být použit pouze uvnitř asynchronních funkcí.

Nejsnazší řešení (ne vždy ideální ;)):

        setTimeout(async function(){
            // Tady už jsme v async/await prostředí (tato funkce je async)
            await sleep(1000)
            alert('Hello')
        })
  • krouzek/async_await.txt
  • Poslední úprava: 2023/11/15 20:54
  • autor: 127.0.0.1