Synchronní vs. Asynchronní kód
Synchronní kód se vykonává řádek po řádku — každý řádek čeká na předchozí. Asynchronní kód umožňuje "odložit" časově náročné operace (síťové požadavky, čtení souborů) a pokračovat dál.
JavaScript používá Event Loop — single-threaded model, kde asynchronní operace (setTimeout, Fetch, I/O) jsou zpracovány na pozadí a jejich výsledky jsou zařazeny do fronty callbacků.
Callback a Callback Hell
Callback je funkce předaná jako argument jiné funkci — zavolá se po dokončení operace. Je to nejstarší async vzor v JS.
// setTimeout — nejjednodušší async operace
console.log("1. Start");
setTimeout(() => {
console.log("3. Po 1 sekundě");
}, 1000);
console.log("2. Hned po setTimeout");
// Výstup: 1, 2, 3 (ne 1, 3, 2!)
// Praktický callback — načtení dat (starý styl)
function nactiUzivatele(id, callback) {
setTimeout(() => {
const uzivatel = { id, jmeno: "Jana" };
callback(null, uzivatel); // (error, data) konvence
}, 500);
}
nactiUzivatele(1, (err, uzivatel) => {
if (err) return console.error(err);
console.log(uzivatel.jmeno);
});
Callback Hell — proč promises vznikly
// ❌ Callback hell — těžko čitelné, těžko laditelné
nactiUzivatele(1, (err, user) => {
if (err) handleErr(err);
nactiObjednavky(user.id, (err, orders) => {
if (err) handleErr(err);
nactiProdukty(orders[0].id, (err, products) => {
if (err) handleErr(err);
zobrazVysledek(products); // 3+ úrovně vnoření!
});
});
});
Promise
Promise je objekt reprezentující budoucí výsledek asynchronní operace. Může být ve třech stavech:
// Vytvoření Promise
const mojPromise = new Promise((resolve, reject) => {
setTimeout(() => {
const uspech = true;
if (uspech) resolve("Data načtena!");
else reject(new Error("Chyba načítání"));
}, 1000);
});
// Konzumace Promise — .then() / .catch() / .finally()
mojPromise
.then(data => console.log(data)) // "Data načtena!"
.catch(err => console.error(err.message))
.finally(() => console.log("Hotovo")); // vždy
// Promise chaining — řeší callback hell
nactiUzivatele(1)
.then(user => nactiObjednavky(user.id))
.then(orders => nactiProdukty(orders[0].id))
.then(products => zobrazVysledek(products))
.catch(err => handleErr(err)); // jeden catch pro vše!
// Promise.all — paralelní požadavky
const [users, products] = await Promise.all([
fetch('/api/users').then(r => r.json()),
fetch('/api/products').then(r => r.json())
]);
// Promise.allSettled — i když některé selžou
const vysledky = await Promise.allSettled([p1, p2, p3]);
vysledky.forEach(r => {
if (r.status === 'fulfilled') console.log(r.value);
else console.error(r.reason);
});
🧠 Kvíz: Co je rozdíl mezi Promise.all() a Promise.allSettled()?
async / await
async/await je syntaktický cukr nad Promises — kód vypadá synchronně, ale chová se asynchronně. Dnes je to standardní způsob práce s async operacemi.
// async funkce vždy vrátí Promise
async function nactiData() {
return "data"; // ekvivalent: return Promise.resolve("data")
}
// await pozastaví funkci (ne celý JS!) dokud Promise nedosáhne settled stavu
async function hlavniFunkce() {
const uzivatel = await nactiUzivatele(1);
const objednavky = await nactiObjednavky(uzivatel.id);
const produkty = await nactiProdukty(objednavky[0].id);
return produkty;
// Čitelné jako synchronní kód!
}
// Paralelní volání s await
async function paralelne() {
// ❌ Sekvenční (pomalé — čeká postupně)
const a = await fetchA();
const b = await fetchB(); // čeká až skončí fetchA
// ✅ Paralelní (rychlé)
const [a, b] = await Promise.all([fetchA(), fetchB()]);
}
// Top-level await (moderní ES2022, v modulech)
const data = await fetch('/api/data').then(r => r.json());
🧠 Kvíz: Co se stane, pokud použijete await mimo async funkci?
Fetch API
Fetch je moderní nativní API pro HTTP požadavky — nahrazuje starý XMLHttpRequest. Vrací Promise.
// GET — načtení dat
async function nactiPrispevky() {
const response = await fetch('https://jsonplaceholder.typicode.com/posts');
// Fetch NESELHÁVÁ při 4xx/5xx — vždy zkontrolujte status!
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json(); // parsuje JSON
return data;
}
// POST — odeslání dat
async function vytvorPrispevek(data) {
const response = await fetch('/api/posts', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'Authorization': 'Bearer ' + token,
},
body: JSON.stringify(data),
});
return response.json();
}
// PUT, PATCH, DELETE — stejný pattern, jiná method
async function smazPrispevek(id) {
await fetch(`/api/posts/${id}`, { method: 'DELETE' });
}
// Query parametry
const params = new URLSearchParams({ page: 1, limit: 10 });
fetch(`/api/posts?${params}`);
// Response typy
response.json() // JSON → objekt
response.text() // text/HTML
response.blob() // binární (obrázky)
response.headers.get('Content-Type')
Error Handling v async kódu
Chyby v async kódu se zpracovávají pomocí try/catch — ekvivalent .catch() u promises, ale čitelnější.
// try/catch/finally s async/await
async function nacteniDat(url) {
try {
const response = await fetch(url);
// fetch nevyhodí chybu pro 4xx/5xx!
if (!response.ok) {
throw new Error(`API chyba: ${response.status}`);
}
const data = await response.json();
return data;
} catch (error) {
// Zachytí: síťové chyby (offline), JSON parse chyby, naše throw
if (error instanceof TypeError) {
console.error('Síťová chyba:', error.message);
} else {
console.error('Obecná chyba:', error.message);
}
return null; // nebo throw pro propagaci výše
} finally {
// Vždy proběhne — ideální pro: skrytí loaderu, cleanup
hideLoader();
}
}
// Vlastní chybové třídy
class APIError extends Error {
constructor(status, message) {
super(message);
this.name = 'APIError';
this.status = status;
}
}
// Utility wrapper — čistší kód bez try/catch všude
async function safeAwait(promise) {
try {
const data = await promise;
return [null, data];
} catch (error) {
return [error, null];
}
}
// Použití:
const [err, data] = await safeAwait(fetch('/api/data'));
if (err) return handleError(err);
// data jsou garantovaně validní
🧠 Kvíz: Fetch vrátí Response s status: 404. Vyhodí to automaticky chybu (rejectuje Promise)?
Praktické cvičení — GitHub API klient
Napište mini aplikaci, která načte data o GitHub uživateli z veřejného API (https://api.github.com/users/JMENO) a zobrazí avatar, jméno, bio, počet repozitářů a followers. Přidejte loading stav a error handling.
async function nactiUzivatele(), try/catch, if (!response.ok) check, loading stav (disable tlačítka + spinner text), zobrazení dat přes innerHTML.Odznak: Async Mistr
Zvládáte Promise, async/await, Fetch API a error handling.
Taháček — rychlá reference
async/await šablona
async function nactiData(url) {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`HTTP ${res.status}`);
return await res.json();
} catch (err) {
console.error(err);
return null;
}
}
Fetch — POST
const res = await fetch('/api/data', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ klic: 'hodnota' }),
});
const data = await res.json();
Paralelní volání
// ✅ Paralelně (rychlé)
const [a, b] = await Promise.all([fetchA(), fetchB()]);
// I při částečných selháních
const results = await Promise.allSettled([p1, p2, p3]);
Zlatá pravidla
- ✅ Vždy
try/catchu async funkcí - ✅ Vždy zkontrolujte
response.okpo fetch - ✅ Paralelní volání pomocí
Promise.all() - ✅
finallypro cleanup (schování loaderu) - ❌ Nikdy
awaitv cyklu pokud lze paralelizovat - ❌ Zapomínat na
await— vrátí Promise, ne hodnotu