Domů O mně Služby Portfolio Učím Blog Kontakt
← Zpět na učím

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:

  1. "Nejdřív musím dojít k bankomatu (async)"
  2. "Až vyberu peníze, můžu jít do obchodu (další async)"
  3. "Až koupím ingredience, můžu je donést domů (další async)"
  4. "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 →

Připraveni začít?

Zaregistrujte se a získejte přístup ke všem dílům tohoto seriálu

Kontaktujte mě

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


Struktura lekcí (souborový strom)

06. Typescript specifika
  • v přípravě
08. Moduly tridy
  • v přípravě
09. React zaklady
  • v přípravě
10. React hooks
  • v přípravě
12. Nextjs server
  • v přípravě
13. Databaze auth
  • v přípravě
14. Nextjs pokrocile
  • v přípravě