07.05 Then a Catch
Výuka
Proč Then a Catch?
V minulé lekci jsme viděli, že Promise je stvrzenka za budoucí hodnotu. Ale jak tu hodnotu použít, když přijde? A co když něco selže?
Představ si to jako:
Máš stvrzenku za burger (Promise). Potřebuješ způsob, jak říct:
- ✅ "Až bude burger hotový, zavolej mi a já ho sním" →
.then() - ❌ "Pokud burgery dojdou, zavolej mi a já si objednám něco jiného" →
.catch()
Promise sám o sobě nedělá nic! Je to jen objekt reprezentující budoucnost. Potřebuješ zaregistrovat callbacky, které se zavolají, až Promise skončí.
const promise = orderBurger(); // Vrátí Promise
// Promise sám nedělá nic viditelného!
// Musíš říct, CO dělat s výsledkem:
promise.then((burger) => {
eat(burger); // Co dělat při úspěchu
});
promise.catch((error) => {
orderPizza(); // Co dělat při chybě
});
Jak .then() funguje?
.then() registruje dva callbacky:
- Fulfillment handler - zavolá se, když Promise uspěje
- Rejection handler (volitelný) - zavolá se, když Promise selže
promise.then(
(value) => { /* úspěch */ }, // fulfillment handler
(error) => { /* chyba */ } // rejection handler (volitelné)
);
Klíčové:
.then()vrací NOVÝ Promise → můžeš řetězit!- Hodnota, kterou vrátíš z
.then(), stane se hodnotou dalšího Promise v řetězu
Promise A ---> .then() ---> Promise B ---> .then() ---> Promise C
↑ ↑
vrací Promise B vrací Promise C
Jak .catch() funguje?
.catch() je zkratka pro .then(null, rejectionHandler):
// Tyto dva zápisy jsou IDENTICKÉ:
promise.then(null, (err) => console.error(err));
promise.catch((err) => console.error(err));
Proč .catch() existuje?
- Čitelnější kód
- Idiomatický způsob pro error handling
- Běžná konvence v Promise řetězech
Důležité: .catch() také vrací nový Promise, takže můžeš pokračovat v řetězení!
promise
.then(value => processValue(value))
.catch(err => console.error(err)) // Zachytí chybu
.then(() => console.log('Pokračuji dál')); // Spustí se i po chybě!
Chaining (Řetězení) - Nejdůležitější koncept!
Každý .then() vrací NOVÝ Promise. To umožňuje lineární zápis místo callback hell:
// ❌ Callback hell (vnořené)
fetchUser((err, user) => {
if (err) return handleError(err);
fetchSettings(user.id, (err, settings) => {
if (err) return handleError(err);
fetchArticles(settings.limit, (err, articles) => {
if (err) return handleError(err);
console.log(articles);
});
});
});
// ✅ Promise chaining (lineární!)
fetchUser()
.then(user => fetchSettings(user.id))
.then(settings => fetchArticles(settings.limit))
.then(articles => console.log(articles))
.catch(err => handleError(err)); // Jedna catch pro VŠE!
Jak to funguje?
fetchUser()vrátí Promise → zavolá se.then()suser- Vrátíš
fetchSettings(user.id)(což je Promise) → automaticky se rozbalí - Až
fetchSettingsskončí → zavolá se další.then()ssettings - A tak dále...
Magická vlastnost: Pokud vrátíš Promise z .then(), následující .then() počká, až se ten Promise vyřeší!
Promise.resolve(21)
.then(v => {
console.log(v); // 21
// Vrátíš Promise, který se vyřeší za 1 sekundu
return new Promise((resolve) => {
setTimeout(() => resolve(v * 2), 1000);
});
})
.then(v => {
console.log(v); // 42 (po 1 sekundě!)
});
Error Propagation (Šíření chyb)
Chyby se propagují řetězem dolů, dokud nenarazí na rejection handler (.catch() nebo druhý parametr .then()):
Promise.resolve(42)
.then(v => {
throw new Error('Oops!'); // Chyba!
})
.then(v => {
console.log('Nikdy se nezavolá'); // Přeskočí se
})
.then(v => {
console.log('Taky ne'); // Přeskočí se
})
.catch(err => {
console.error('Chytil jsem chybu!', err.message); // 'Oops!'
});
Co se stalo?
- První
.then()vyhodil chybu - Promise z prvního
.then()se stal rejected - Další
.then()callbacky se přeskočily (mají jen fulfillment handler) .catch()chybu zachytil
Klíčové pravidlo:
- Pokud
.then()nemá rejection handler, použije se defaultní, který chybu propaguje dál:// Default rejection handler: (err) => { throw err; } // Jen přehází chybu dál
Default Handlers
Pokud vynecháš handler v .then(), použije se defaultní:
1. Default fulfillment handler (když vynecháš první parametr):
// Default fulfillment handler:
(value) => value // Jen předá hodnotu dál
Příklad:
Promise.resolve(42)
.then(null, (err) => console.error(err)) // Jen rejection handler
.then(v => console.log(v)); // 42 - hodnota prošla skrz!
2. Default rejection handler (když vynecháš druhý parametr):
// Default rejection handler:
(err) => { throw err; } // Propaguje chybu dál
Příklad:
Promise.reject('Chyba!')
.then(v => console.log(v)) // Žádný rejection handler → použije default
.catch(err => console.error(err)); // 'Chyba!' - chyba prošla skrz
Problém: Polknuté chyby (Swallowed Errors)
Pozor! Chyby se snadno ztratí, pokud zapomeneš .catch():
Promise.resolve(42)
.then(v => {
console.log(v.toLowerCase()); // CHYBA! Number nemá toLowerCase()
});
// Chyba se "polkne" - nikdo ji nevidí! 😱
Proč?
- Chyba v
.then()odmítne Promise, který.then()vrátil - Ale my jsme ten Promise nezachytili!
- Chyba zmizí do prázdna...
Řešení: VŽDY končit řetěz .catch()
Promise.resolve(42)
.then(v => {
console.log(v.toLowerCase()); // CHYBA!
})
.catch(err => {
console.error('Zachytil jsem chybu!', err); // ✅ Vidíme ji!
});
Best practice:
fetchData()
.then(processData)
.then(saveData)
.catch(handleAllErrors); // Zachytí chyby ze všech kroků!
Klíčové koncepty
.then(onFulfilled, onRejected)- Zaregistruje callbacky pro úspěch/chybu.catch(onRejected)- Zkratka pro.then(null, onRejected)- Chaining - Každý
.then()/.catch()vrací nový Promise - Value passing - Hodnota z
returnse stane hodnotou dalšího Promise - Promise unwrapping - Pokud vrátíš Promise, automaticky se rozbalí
- Error propagation - Chyby se šíří řetězem dolů k první
.catch() - Default handlers - Vynechaný handler → použije se default (pass-through nebo rethrow)
JavaScript
Základní použití .then()
// Promise, který se vyřeší za 1 sekundu
const promise = new Promise((resolve) => {
setTimeout(() => {
resolve('Hotovo!');
}, 1000);
});
// Zaregistruj fulfillment handler
promise.then((value) => {
console.log(value); // 'Hotovo!' (po 1s)
});
console.log('Pokračuji dál...'); // Vypíše se HNED
// Výstup:
// Pokračuji dál...
// (po 1s): Hotovo!
Co se stalo?
promiseje Pending (čeká se na timeout).then()zaregistruje callback - uloží si ho pro později- Program pokračuje (neblokuje!)
- Po 1 sekundě se Promise vyřeší → callback se zavolá
.then() s oběma handlery
const promise = new Promise((resolve, reject) => {
const success = Math.random() > 0.5;
setTimeout(() => {
if (success) {
resolve('Úspěch!');
} else {
reject('Chyba!');
}
}, 1000);
});
promise.then(
(value) => {
console.log('✅', value); // Úspěch!
},
(error) => {
console.error('❌', error); // Chyba!
}
);
// Buď:
// ✅ Úspěch!
// Nebo:
// ❌ Chyba!
Chaining - Předávání hodnot
Promise.resolve(10)
.then((num) => {
console.log('Krok 1:', num); // 10
return num * 2; // Vrátíš hodnotu
})
.then((num) => {
console.log('Krok 2:', num); // 20
return num + 5;
})
.then((num) => {
console.log('Krok 3:', num); // 25
return num / 5;
})
.then((num) => {
console.log('Finální:', num); // 5
});
// Výstup:
// Krok 1: 10
// Krok 2: 20
// Krok 3: 25
// Finální: 5
Co se stalo?
- Každý
.then()vrací hodnotu - Ta hodnota se stane hodnotou dalšího Promise
- Lineární tok dat! ✅
Chaining - Async kroky
function delay(ms, value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value), ms);
});
}
console.log('Start');
delay(1000, 'Krok 1')
.then((result) => {
console.log(result); // 'Krok 1' (po 1s)
return delay(500, 'Krok 2'); // Vrátíš Promise!
})
.then((result) => {
console.log(result); // 'Krok 2' (po další 0.5s)
return delay(300, 'Krok 3');
})
.then((result) => {
console.log(result); // 'Krok 3' (po další 0.3s)
console.log('Konec');
});
// Výstup:
// Start
// (po 1s): Krok 1
// (po 0.5s): Krok 2
// (po 0.3s): Krok 3
// Konec
Co se stalo?
- Vrátíš-li Promise z
.then(), další.then()počká, až se vyřeší! - Async operace za sebou, ale lineární zápis (ne callback hell)
Error handling s .catch()
Promise.resolve('OK')
.then((value) => {
console.log(value); // 'OK'
throw new Error('Něco se pokazilo!');
})
.then((value) => {
console.log('Nikdy se nezavolá'); // Přeskočí se
})
.catch((error) => {
console.error('Chyba:', error.message); // 'Něco se pokazilo!'
return 'Opraveno'; // Můžeš vrátit hodnotu!
})
.then((value) => {
console.log('Pokračuji:', value); // 'Opraveno'
});
// Výstup:
// OK
// Chyba: Něco se pokazilo!
// Pokračuji: Opraveno
Co se stalo?
- První
.then()vyhodil chybu → Promise rejected - Druhý
.then()se přeskočil (žádný rejection handler) .catch()chybu zachytil a vrátil'Opraveno'- Promise z
.catch()se stal Fulfilled s hodnotou'Opraveno' - Další
.then()pokračoval normálně
Reálný příklad - HTTP request chain
// Simulace HTTP requestu
function fetchUser(id) {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (id > 0) {
resolve({ id, name: 'Jan', settingsId: 42 });
} else {
reject(new Error('Neplatné ID'));
}
}, 500);
});
}
function fetchSettings(settingsId) {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ theme: 'dark', lang: 'cs' });
}, 300);
});
}
// Promise chain
fetchUser(1)
.then((user) => {
console.log('Uživatel:', user.name);
return fetchSettings(user.settingsId); // Vrátíš Promise
})
.then((settings) => {
console.log('Nastavení:', settings.theme);
})
.catch((error) => {
console.error('Chyba:', error.message);
});
// Výstup (po ~800ms):
// Uživatel: Jan
// Nastavení: dark
Problém: Polknutá chyba
// ❌ CHYBA SE ZTRATÍ!
Promise.resolve(42)
.then((num) => {
return num.toUpperCase(); // CHYBA! Number nemá toUpperCase()
});
// ... nic! Chyba se "polkla", nikdo ji neuvidí 😱
Oprava:
// ✅ Chyba se zachytí
Promise.resolve(42)
.then((num) => {
return num.toUpperCase(); // CHYBA!
})
.catch((err) => {
console.error('Zachyceno:', err.message); // ✅
});
TypeScript
TypeScript přidává typovou bezpečnost pro Promise hodnoty a error handling.
Typované .then() callbacky
// Promise<number>
const promise: Promise<number> = Promise.resolve(42);
promise.then((value: number) => {
console.log(value.toFixed(2)); // ✅ TS ví, že value je number
// console.log(value.toUpperCase()); // ❌ Error
});
TypeScript automaticky odvodí typy:
Promise.resolve(42)
.then((value) => {
// TS ví, že value: number
return value * 2;
})
.then((value) => {
// TS ví, že value: number (vrátil jsi number)
console.log(value);
});
Chaining s různými typy
interface User {
id: number;
name: string;
}
interface Settings {
theme: string;
lang: string;
}
function fetchUser(id: number): Promise<User> {
return Promise.resolve({ id, name: 'Jan' });
}
function fetchSettings(userId: number): Promise<Settings> {
return Promise.resolve({ theme: 'dark', lang: 'cs' });
}
// TS sleduje typy v celém řetězu
fetchUser(1)
.then((user: User) => {
console.log(user.name); // ✅ TS ví, že user má name
return fetchSettings(user.id); // Vrací Promise<Settings>
})
.then((settings: Settings) => {
console.log(settings.theme); // ✅ TS ví, že settings má theme
});
Error handling s typy
// Typ pro chybu
class ApiError extends Error {
constructor(public code: number, message: string) {
super(message);
}
}
function fetchData(): Promise<string> {
return new Promise((resolve, reject) => {
const success = Math.random() > 0.5;
if (success) {
resolve('Data loaded');
} else {
reject(new ApiError(404, 'Not found'));
}
});
}
fetchData()
.then((data: string) => {
console.log(data.toUpperCase()); // ✅
})
.catch((error: Error) => {
// TS ví, že error je Error (nebo jeho podtyp)
console.error(error.message);
if (error instanceof ApiError) {
console.error('API Error code:', error.code);
}
});
Generické Promise funkce
// Generická funkce vracející Promise
function wrapInPromise<T>(value: T): Promise<T> {
return Promise.resolve(value);
}
wrapInPromise(42)
.then((num) => {
// TS ví, že num: number
console.log(num * 2);
});
wrapInPromise('hello')
.then((str) => {
// TS ví, že str: string
console.log(str.toUpperCase());
});
Typování .catch() handleru
// .catch() vždy dostává unknown (nebo any v některých TS verzích)
Promise.reject(new Error('Oops'))
.catch((error: unknown) => {
// Musíš zkontrolovat typ před použitím
if (error instanceof Error) {
console.error(error.message); // ✅ Bezpečné
}
});
Rozdíl JS vs TS
JavaScript:
.then()a.catch()bez typů- Nevíš, jaký typ hodnoty dostaneš v callbacku
- Chyby až za běhu
- Žádná kontrola, co můžeš s hodnotou dělat
TypeScript:
Promise- přesný typ hodnoty- Autocomplete v IDE pro hodnoty v
.then() - Kontrola typů při kompilaci
- Vynucuje správné použití hodnot
Příklad:
// JS - nevíš co dostaneš
fetchData().then((data) => {
console.log(data.toUpperCase()); // Může selhat za běhu
});
// TS - víš přesně
fetchData().then((data: string) => {
console.log(data.toUpperCase()); // ✅ TS ví, že je to string
// console.log(data.toFixed()); // ❌ Error - string nemá toFixed
});
Tip
💡 .catch() vs druhý parametr .then():
// ❌ Toto NEZACHYTÍ chyby z prvního handleru!
promise.then(
(value) => {
throw new Error('Chyba v handleru'); // Nezachytí se!
},
(error) => {
console.error(error); // Nikdy se nezavolá
}
);
// ✅ Toto zachytí chyby z CELÉHO řetězu
promise
.then((value) => {
throw new Error('Chyba v handleru'); // Zachytí se!
})
.catch((error) => {
console.error(error); // ✅ Zavolá se
});
Pravidlo: Používej .catch() na konci řetězu, ne druhý parametr .then()!
💡 Vždy končit řetěz s .catch():
// ✅ Best practice
fetchData()
.then(processData)
.then(saveData)
.then(showSuccess)
.catch(handleError); // Zachytí chyby ze všech kroků
💡 Recovery pattern (zotavení z chyby):
fetchPrimaryAPI()
.catch((error) => {
console.warn('Primary API failed, using fallback');
return fetchFallbackAPI(); // Zkus záložní API
})
.then((data) => {
console.log('Data (z primary nebo fallback):', data);
});
💡 Chaining hodnot vs Promises:
// Hodnota → další .then() dostane hodnotu HNED
.then(() => 42)
// Promise → další .then() POČKÁ, až se Promise vyřeší
.then(() => Promise.resolve(42))
// Async funkce → vrací Promise → další .then() počká
.then(async () => 42)
💡 Default fulfillment handler (pass-through):
Promise.resolve(42)
.then(null, (err) => console.error(err)) // Jen rejection handler
.then((value) => console.log(value)); // 42 - hodnota "prošla skrz"
// Ekvivalentní s:
Promise.resolve(42)
.then((v) => v, (err) => console.error(err)) // Explicitní pass-through
.then((value) => console.log(value));
Kvíz
Které výroky o .then() a .catch() jsou pravdivé?
✅ - Každý .then() vrací nový Promise → můžeš řetězit .then() za .then()
✅ - .catch(fn) je přesně stejné jako .then(null, fn) - jen čitelnější syntax
❌ - Chyby se PROPAGUJÍ dál - pokud není rejection handler, použije se default, který chybu rethrowne (přehodí dál)
✅ - Promise unwrapping - pokud vrátíš Promise, automaticky se rozbalí a další .then() počká na jeho vyřešení
🎯 Závěrečný projekt
Po dokončení všech 8 dílů vytvoříte jednoduchou Todо aplikaci v čistém JavaScriptu. Naučíte se, jak aplikovat vše, co jste se naučili, na reálný projekt.
Zobrazit podrobnosti projektu →Informace o seriálu
Obtížnost
Délka
Cca 480 minut
Počet videí
8 videí + projekty
Certifikát
Po dokončení obdržíte certifikát
Lekce v této sekci
- 7.1 Synchronní vs asynchronní programování
- 7.2 Callbacks
- 07.03 Callback Hell
- 07.04 Promises úvod
- 07.05 Then a Catch
- 07.06 Promise.all
- 07.07 Promise.race
- 07.08 Async/Await
- 07.09 Try/Catch s async
- 07.10 Fetch API
- 7.11 Generátory
Struktura lekcí (souborový strom)
- 1.1 Úvod do JavaScriptu a TypeScriptu
- 1.2 Nastavení prostředí
- 1.3 První program
- 1.4 Proměnné: var, let, const
- 1.5 Datové typy - přehled
- 1.6 String (řetězce)
- 1.7 Number (čísla)
- 1.8 Boolean (pravda/nepravda)
- 1.9 Null a Undefined
- 1.10 Type Inference vs Annotations
- 1.11 Aritmetické operátory
- 1.12 Porovnávací operátory
- 1.13 Logické operátory
- 1.14 Komentáře
- 1.15 Console metody
- 03.01 Deklarace funkce
- 03.02 Function Expression
- 03.03 Arrow Functions
- 03.04 Parametry a argumenty
- 03.05 Return hodnoty
- 03.06 Výchozí parametry
- 03.07 Rest parametry
- 03.08 Co je Scope
- 03.09 Lexikální Scope
- 03.10 Řetězec Scope
- 03.11 Globální Scope
- 03.12 Životní Cyklus Proměnných
- 03.13 Omezení Scope
- 03.14 Použití Closures
- 03.15 Callback funkce
- 03.16 Higher-order Functions
- 03.17 IIFE
- 03.18 Void funkce
- 03.19 Rekurze
- v přípravě
- v přípravě
- v přípravě
- v přípravě
- 01 — Co je Next.js
- 02 — Vytvoření projektu
- 03 — Struktura projektu (app/)
- 04 — Page komponenty (page.js / page.tsx)
- 05 — Layout komponenty (layout.js / layout.tsx)
- 06 — File-based routing
- 07 — Dynamické routy ([id]/page.js)
- 08 — Link komponenta (navigace)
- 09 — Image komponenta (next/image)
- 10 — Metadata (title, description, Open Graph)
- 11 — Loading UI (loading.js / loading.tsx)
- 12 — Error handling (error.js / error.tsx)
- 13 — Not Found (not-found.js / not-found.tsx)
- v přípravě
- v přípravě
- v přípravě