07.03 Callback Hell
Výuka
Co je Callback Hell?
Callback Hell (také "Pyramid of Doom" = pyramida zkázy) je situace, kdy máš vnořené callbacky v callbackech v callbackech...
Kód začne vypadat jako pyramida ležící na boku → ▶ a stává se nečitelným a neudržovatelným.
Představ si to jako:
Jdeš koupit jídlo, ale každý krok závisí na předchozím:
- "Nejdřív musím dojít k bankomatu (async)"
- "Až vyberu peníze, můžu jít do obchodu (další async)"
- "Až koupím ingredience, můžu je donést domů (další async)"
- "Až je donesu, můžu vařit (další async)"
Každý krok musí čekat na předchozí. S callbacky to vypadá takto:
vyberPenize(() => {
jdiDoObchodu(() => {
koupIngredience(() => {
donesDomů(() => {
vař(() => {
jez(() => {
// Konečně! Ale kde vlastně jsem v kódu?? 😵
});
});
});
});
});
});
Proč je Callback Hell problém?
1. Nečitelnost 📖
Když čteš kód, musíš skákat nahoru a dolů, hledat závorky, sledovat vnořování. Nemůžeš číst kód lineárně.
doA(() => {
doC();
doD(() => {
doF();
});
doE();
});
doB();
Otázka: V jakém pořadí se to provede?
Odpověď: A → B → C → D → E → F
WTF?! Tohle nikdo nemůže rychle pochopit! 🤯
2. "Ztratil jsem se v kódu"
Čím víc vnořených callbacků, tím hůř víš:
- Kde jsi v programu
- Co se stane dál
- Jak zpracovat chyby
3. Error handling katastrofa
Musíš kontrolovat chyby v každém callbacku zvlášť:
readFile('file1.txt', (err1, data1) => {
if (err1) { /* zpracuj chybu 1 */ }
readFile('file2.txt', (err2, data2) => {
if (err2) { /* zpracuj chybu 2 */ }
readFile('file3.txt', (err3, data3) => {
if (err3) { /* zpracuj chybu 3 */ }
// Teprve tady můžeš použít data
});
});
});
4. Náš mozek nefunguje takhle
Náš mozek plánuje lineárně (krok po kroku):
"Udělám A, pak B, pak C."
Ale callback hell nás nutí přemýšlet nelineárně a vnořeně:
"Udělám A, a uvnitř A je B, a uvnitř B je C, a..."
Jak to funguje? (Proč to vzniká)
Představ si, že potřebuješ 3 soubory za sebou:
1. Přečti config.json
2. Podle config přečti users.json
3. Podle users přečti prvního uživatele
Každý krok závisí na předchozím → musíš čekat → vnořuješ callbacky:
readFile('config.json', (err, config) => {
// Teď máš config, můžeš číst users
readFile(config.usersFile, (err, users) => {
// Teď máš users, můžeš číst detail prvního
readFile(users[0].file, (err, detail) => {
// Konečně! Ale jsi 3 úrovně hluboko...
console.log(detail);
});
});
});
Každá asynchronní operace přidává úroveň vnořování → pyramida roste → hell! 🔥
JavaScript
Příklad Callback Hell
// Chceme: načíst uživatele → jeho nastavení → jeho oblíbené články
// Každý krok je async a závisí na předchozím
getUserById(1, (err, user) => {
if (err) {
console.error('Chyba načtení uživatele:', err);
return;
}
console.log('Mám uživatele:', user.name);
getUserSettings(user.id, (err, settings) => {
if (err) {
console.error('Chyba načtení nastavení:', err);
return;
}
console.log('Mám nastavení:', settings);
getFavoriteArticles(user.id, settings.limit, (err, articles) => {
if (err) {
console.error('Chyba načtení článků:', err);
return;
}
console.log('Mám články:', articles);
// A co když potřebuješ ještě další úroveň??
// → Další callback → Ještě hlubší vnořování
});
});
});
// Pyramida → ▶
Co se stalo?
- 3 úrovně vnořování
- 3x kontrola chyb (if err)
- Těžko čitelné
- Těžko rozšiřitelné (přidej další krok = další vnořování)
Extrémní příklad (7 úrovní!)
// Skutečný příklad z reálného projektu 😱
listen('click', function handler(evt) {
setTimeout(function request() {
ajax('http://some.url.1', function response(text) {
if (text == 'hello') {
handler();
} else if (text == 'world') {
request();
} else {
setTimeout(function timeout() {
ajax('http://some.url.2', function response2(text2) {
if (text2 == 'retry') {
timeout();
} else {
console.log(text2);
}
});
}, 500);
}
});
}, 500);
});
Tohle JE callback hell! Nikdo tohle nedokáže rychle pochopit. 💀
Problém: Pořadí není zřejmé
doA(function() {
doC();
doD(function() {
doF();
});
doE();
});
doB();
Zkus uhodnout pořadí PŘED tím, než si přečteš odpověď:
. . . .
Odpověď: A → B → C → D → E → F
Vidíš problém? Kód vypadá jako C → D → F → E, ale skutečné pořadí je jiné!
TypeScript
TypeScript nezachrání před callback hell - máš stále stejný problém, jen s typy.
Callback Hell s typy
interface User {
id: number;
name: string;
}
interface Settings {
theme: string;
limit: number;
}
interface Article {
id: number;
title: string;
}
// Stejný callback hell, jen typovaný
getUserById(1, (err: Error | null, user: User | null) => {
if (err || !user) {
console.error('Chyba:', err);
return;
}
getUserSettings(user.id, (err: Error | null, settings: Settings | null) => {
if (err || !settings) {
console.error('Chyba:', err);
return;
}
getFavoriteArticles(
user.id,
settings.limit,
(err: Error | null, articles: Article[] | null) => {
if (err || !articles) {
console.error('Chyba:', err);
return;
}
console.log('Hotovo:', articles);
// Stále callback hell, jen s typy!
}
);
});
});
TypeScript přidává:
- ✅ Typovou bezpečnost (víš, co dostaneš)
- ✅ Autocomplete v IDE
- ❌ NEŘEŠÍ problém čitelnosti a vnořování
Rozdíl JS vs TS
JavaScript:
- Callback hell existuje
- Žádné typy → nevíš co dostaneš v callbacku
- Chyby až za běhu
TypeScript:
- Callback hell stále existuje (stejný problém!)
- Máš typy → víš co dostaneš
- Chyby při compilaci
Klíčové: TypeScript NEŘEŠÍ callback hell! Jen ti dá typovou bezpečnost.
// TS ti pomůže s typy, ale ne s vnořováním
readFile('file.txt', (err: Error | null, data: string | null) => {
// ✅ TS ví, že data je string | null
// ❌ Stále vnořený callback hell
readFile('file2.txt', (err2, data2) => {
readFile('file3.txt', (err3, data3) => {
// Pyramida zkázy!
});
});
});
Řešení Callback Hell
❌ Špatná řešení:
1. "Rozděl na pojmenované funkce"
function step1() { readFile('a', step2); }
function step2() { readFile('b', step3); }
function step3() { readFile('c', done); }
function done() { console.log('Hotovo'); }
step1(); // Musíš skákat mezi funkcemi - stále nepřehledné
2. "Používej error-first callbacks"
// Stále máš vnořování, jen s konzistentním error handlingem
readFile('a', (err, data) => {
if (err) return handleError(err);
readFile('b', (err, data) => {
if (err) return handleError(err);
// Stále vnořené!
});
});
✅ Správná řešení:
1. Promises (příští lekce!)
readFile('a')
.then(data => readFile('b'))
.then(data => readFile('c'))
.then(data => console.log('Hotovo'))
.catch(err => console.error(err));
// Lineární, čitelné! 🎉
2. Async/await (lekce 7.8)
try {
const a = await readFile('a');
const b = await readFile('b');
const c = await readFile('c');
console.log('Hotovo');
} catch (err) {
console.error(err);
}
// Vypadá jako synchronní kód! 🚀
Tip
💡 Callback hell = signál že potřebuješ Promises:
// ❌ Callback hell
step1(() => {
step2(() => {
step3(() => {
done();
});
});
});
// ✅ Promises
step1()
.then(step2)
.then(step3)
.then(done);
💡 "Pyramid of Doom" test:
Pokud tvůj kód vypadá jako pyramida na boku (▶), máš callback hell:
function() {
function() {
function() {
function() {
// ▶ Pyramida!
💡 Pravidlo: Max 2 úrovně vnořování
// ✅ OK - 1 úroveň
fetchUser(id, (err, user) => {
console.log(user);
});
// ⚠️ Hranice - 2 úrovně
fetchUser(id, (err, user) => {
fetchSettings(user.id, (err, settings) => {
console.log(settings);
});
});
// ❌ Callback hell - 3+ úrovně
fetchUser(id, (err, user) => {
fetchSettings(user.id, (err, settings) => {
fetchArticles(user.id, (err, articles) => {
// Stop! Použij Promises!
});
});
});
💡 Historická poznámka:
Callback hell byl hlavní důvod, proč vznikly Promises (ES6, 2015) a async/await (ES2017)!
Kvíz
Které výroky o Callback Hell jsou pravdivé?
✅ - Při 3+ úrovních vnořování vzniká callback hell (pyramida zkázy)
❌ - TypeScript přidá typy, ale NEŘEŠÍ vnořování - callback hell stále existuje
✅ - Kód funguje, ale je nečitelný a neudržovatelný - proto je to problém
✅ - Promises (a async/await) byly vytvořeny právě jako řešení callback hell
🎯 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ě