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') })