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

07.09 Try/Catch s async

Výuka

Proč Try/Catch s async?

V předchozí lekci jsme se naučili async/await - čisté a čitelné vyjádření asynchronního kódu. Ale co se stane, když něco selže? Když Promise skončí rejection místo fulfillment?

Při klasických Promises jsme používali .catch():

fetchData()
  .then(data => zpracujData(data))
  .catch(error => console.error(error));

S async/await ale nemáme .catch() k dispozici - píšeme kód, který vypadá synchronně. Naštěstí JavaScript má pro synchronní kód osvědčený mechanismus zachycení chyb: try/catch.

Představ si to jako: Záchranná síť pod provazochodcem. Když akrobat (tvůj kód) balancuje na laně (asynchronní operace), síť (try/catch) ho zachytí, pokud spadne (nastane chyba). Bez sítě by spadl až na zem (unhandled rejection → crash/varování).

Jak to funguje?

1. Obalíš "nebezpečný" await kód do bloku try { }
2. Pod něj přidáš catch(error) { } pro zachycení chyb
3. Když await vrátí rejected Promise  program skočí do catch
4. Volitelně finally { } pro kód, který se provede VŽDY

Klíčový princip: Když await čeká na Promise, který skončí rejection, JavaScript "hodí" (throw) tuto chybu - jako kdyby tam byl synchronní throw. Blok try/catch ji zachytí.

Rozdíl oproti .catch()

Aspekt .catch() try/catch
Styl kódu Funkcionální (řetězení) Imperativní (blokový)
Čitelnost Méně podobné běžnému kódu Vypadá jako běžný synchronní kód
Scope Zachytí chyby v řetězci Zachytí chyby v bloku try
Použití S .then() S async/await

Klíčové koncepty

  • try - "Zkus provést tento kód, ale může selhat"
  • catch - "Když kód selže, tady zachytím chybu"
  • finally - "Tento kód proveď VŽDY, ať už úspěch nebo chyba"
  • throw - "Vyhazuji chybu (rejection se převede na throw)"

Anatomie error objektu

Když catchneš error z Promise rejection, dostaneš buď:

  • Error objekt s .message a .stack
  • Libovolnou hodnotu (pokud někdo udělal reject("text"))

Proto je dobré vždy kontrolovat, s čím pracuješ!


JavaScript

// Základní try/catch s async/await
async function nactiUzivatele(id) {
  try {
    // Nebezpečná operace - může selhat
    const response = await fetch(`/api/users/${id}`);

    // I úspěšný fetch může vrátit chybový status
    if (!response.ok) {
      throw new Error(`HTTP error! status: ${response.status}`);
    }

    const user = await response.json();
    return user;

  } catch (error) {
    // Všechny chyby skončí tady
    console.error("Nepodařilo se načíst uživatele:", error.message);

    // Můžeme vrátit výchozí hodnotu
    return null;

  } finally {
    // Provede se VŽDY (i po return!)
    console.log("Operace dokončena");
  }
}

// Použití
async function main() {
  const user = await nactiUzivatele(123);

  if (user) {
    console.log("Uživatel:", user.name);
  } else {
    console.log("Uživatel nenalezen");
  }
}

main();

Co se stane?

  1. fetch() může selhat (síťová chyba, timeout, CORS...)
  2. Pokud selže → await "hodí" chybu → skočíme do catch
  3. response.json() může selhat (nevalidní JSON)
  4. Pokud cokoliv selže → error obsahuje informace
  5. finally se provede VŽDY - ideální pro úklid

Zachycení více typů chyb

async function zpracujData() {
  try {
    const response = await fetch('/api/data');
    const data = await response.json();

    // Tady může být další logika
    return data;

  } catch (error) {
    // Rozlišení typu chyby
    if (error instanceof TypeError) {
      // Síťová chyba (fetch selhal úplně)
      console.error("Problém se sítí:", error.message);
    } else if (error instanceof SyntaxError) {
      // JSON parsing selhal
      console.error("Server vrátil nevalidní data");
    } else {
      // Jiný typ chyby
      console.error("Neočekávaná chyba:", error);
    }

    // Re-throw pokud nechceme chybu "spolknout"
    throw error;
  }
}

Nebezpečí: Paralelní await bez správného catchování

// ❌ PROBLÉM: Druhý await neproběhne, pokud první selže
async function spatne() {
  try {
    const user = await fetchUser();     // Pokud selže...
    const posts = await fetchPosts();   // ...toto se vůbec nespustí
    return { user, posts };
  } catch (error) {
    // Nezachytíme chybu z fetchPosts, pokud fetchUser uspěl
    // ale fetchPosts selhal až "mezitím"
  }
}

// ✅ SPRÁVNĚ: Promise.all pro paralelní operace
async function lepe() {
  try {
    const [user, posts] = await Promise.all([
      fetchUser(),
      fetchPosts()
    ]);
    return { user, posts };
  } catch (error) {
    // Zachytí chybu z KTERÉHOKOLIV Promise
    console.error("Něco selhalo:", error);
  }
}

TypeScript

// TypeScript: Typované chyby a výsledky

// Definice vlastní chybové třídy
class ApiError extends Error {
  constructor(
    message: string,
    public statusCode: number,
    public endpoint: string
  ) {
    super(message);
    this.name = "ApiError";
  }
}

// Typovaný interface pro odpověď
interface User {
  id: number;
  name: string;
  email: string;
}

// Funkce s jasně definovanými typy
async function nactiUzivatele(id: number): Promise<User | null> {
  try {
    const response = await fetch(`/api/users/${id}`);

    if (!response.ok) {
      throw new ApiError(
        `Failed to fetch user`,
        response.status,
        `/api/users/${id}`
      );
    }

    const user: User = await response.json();
    return user;

  } catch (error: unknown) {
    // TypeScript 4.4+: error je typu 'unknown'
    // Musíme ho správně zkontrolovat

    if (error instanceof ApiError) {
      console.error(
        `API chyba (${error.statusCode}): ${error.message}`
      );
    } else if (error instanceof Error) {
      console.error(`Obecná chyba: ${error.message}`);
    } else {
      console.error(`Neznámá chyba:`, error);
    }

    return null;
  }
}

// Alternativa: Result pattern (žádné výjimky)
type Result<T, E = Error> =
  | { success: true; data: T }
  | { success: false; error: E };

async function nactiUzivateleSafe(id: number): Promise<Result<User, ApiError>> {
  try {
    const response = await fetch(`/api/users/${id}`);

    if (!response.ok) {
      return {
        success: false,
        error: new ApiError("Fetch failed", response.status, `/api/users/${id}`)
      };
    }

    const user: User = await response.json();
    return { success: true, data: user };

  } catch (error) {
    return {
      success: false,
      error: new ApiError(
        error instanceof Error ? error.message : "Unknown error",
        0,
        `/api/users/${id}`
      )
    };
  }
}

// Použití Result pattern
async function main(): Promise<void> {
  const result = await nactiUzivateleSafe(123);

  if (result.success) {
    console.log("Uživatel:", result.data.name);
  } else {
    console.error("Chyba:", result.error.message);
  }
}

TypeScript přidává:

  • error: unknown v catch bloku - nuceni jsme zkontrolovat typ
  • ✅ Vlastní error třídy s typed properties (statusCode, endpoint)
  • Result pattern - explicitní práce s úspěchem/chybou bez výjimek
  • ✅ Typovaná návratová hodnota Promise

Rozdíl JS vs TS

JavaScript:

  • catch(error) - error je implicitně any
  • Můžeš s ním dělat cokoliv (včetně přístupu k neexistujícím vlastnostem)
  • Žádná kontrola typu při kompilaci

TypeScript:

  • catch(error: unknown) - musíš zkontrolovat typ před použitím
  • Vlastní error třídy s typovanými vlastnostmi
  • IDE ti napovídá dostupné vlastnosti po type narrowing
// TypeScript rozdíl v praxi

// ❌ JS přístup - nebezpečné
catch (error) {
  console.log(error.response.data.message);
  // Pokud error nemá response → runtime crash!
}

// ✅ TS přístup - bezpečné
catch (error: unknown) {
  if (
    error instanceof Error &&
    "response" in error &&
    typeof (error as any).response?.data?.message === "string"
  ) {
    console.log((error as any).response.data.message);
  } else {
    console.log("Neznámá chyba");
  }
}

Tip

💡 Nepolykat chyby bezdůvodně:

// ❌ Špatně - chyba zmizí, nikdo se nedozví
async function spatne() {
  try {
    await nebezpecnaOperace();
  } catch (error) {
    // Prázdný catch = spolknutá chyba!
  }
}

// ✅ Správně - alespoň zalogovat, nebo předat dál
async function lepe() {
  try {
    await nebezpecnaOperace();
  } catch (error) {
    console.error("Operace selhala:", error);
    throw error; // Předáme dál, pokud je to vhodné
  }
}

💡 Finally pro úklid zdrojů:

async function pracujSSouborem() {
  let file = null;

  try {
    file = await otevriSoubor("data.txt");
    const obsah = await file.read();
    return zpracuj(obsah);
  } catch (error) {
    console.error("Chyba při práci se souborem:", error);
    return null;
  } finally {
    // Zavři soubor VŽDY - i při úspěchu, i při chybě
    if (file) {
      await file.close();
    }
  }
}

💡 Globální handler pro nezachycené Promise rejection:

// V prohlížeči
window.addEventListener("unhandledrejection", (event) => {
  console.error("Nezachycená Promise rejection:", event.reason);
  // Logování do analytiky, zobrazení uživateli, atd.
});

// V Node.js
process.on("unhandledRejection", (reason, promise) => {
  console.error("Nezachycená Promise rejection:", reason);
});

Kvíz

Co se stane, když await čeká na Promise, který skončí rejection?

- Nezachycená rejection nezpůsobí návrat undefined, způsobí výjimku

- Přesně tak! await na rejected Promise způsobí "throw", který lze zachytit try/catch blokem

- Chyba JDE zachytit pomocí try/catch (nebo globálního handleru)

- V prohlížeči se zobrazí varování, ale kód nepokračuje normálně. V novějších verzích Node.js může nezachycená rejection i ukončit proces

🎯 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ě