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

07.08 Async/Await

Výuka

Proč Async/Await?

Promises vyřešily callback hell, ale stále vypadají asynchronně:

// ❌ Promise chain - lepší než callbacks, ale stále async syntax
fetchUser()
  .then(user => fetchSettings(user.id))
  .then(settings => fetchArticles(settings.limit))
  .then(articles => console.log(articles))
  .catch(handleError);

Problém: Náš mozek přemýšlí synchronně (krok po kroku), ale kód vypadá asynchronně (.then() chains).

Co kdybychom mohli psát async kód, který VYPADÁ jako synchronní?

// ✅ Async/await - vypadá jako synchronní kód!
async function loadData() {
  const user = await fetchUser();
  const settings = await fetchSettings(user.id);
  const articles = await fetchArticles(settings.limit);
  console.log(articles);
}

Async/await je syntactic sugar (syntaktický cukr) nad Promises - pod kapotou jsou to stále Promises, ale syntax je mnohem čitelnější!

Představ si to jako:

Máš recept na vaření:

❌ S Promises (.then()):

1. Sežeň vajíčka.then(() => {
  2. Rozklepni vajíčka.then(() => {
    3. Vař vejce.then(() => {
      4. Sněz vejce
    })
  })
})

✅ S Async/Await:

async function vařVejce() {
  const vajíčka = await sežeňVajíčka();
  const rozklepnutá = await rozklepniVajíčka(vajíčka);
  const uvařená = await vařVejce(rozklepnutá);
  snězVejce(uvařená);
}

Druhý způsob je přirozenější! Čte se shora dolů, jako běžný recept.

Co je async funkce?

async funkce je funkce, která:

  1. Vždy vrací Promise (i když uvnitř vrátíš hodnotu)
  2. Může používat await uvnitř

Syntaxe:

async function nazev() {
  // Můžeš používat await
}

Pravidla:

  • async před function → funkce vrací Promise
  • Vrátíš-li hodnotu → zabalí se do Promise.resolve(hodnota)
  • Vyhodíš-li chybu → zabalí se do Promise.reject(chyba)

Příklad:

async function getNumber() {
  return 42;  // Vypadá jako return hodnoty
}

// Ale ve skutečnosti:
getNumber();  // → Promise.resolve(42)

// Použití:
getNumber().then(num => console.log(num));  // 42

Je to ekvivalentní s:

function getNumber() {
  return Promise.resolve(42);
}

Co je await?

await = "počkej na vyřešení Promise"

Pravidla:

  1. Pouze v async funkcích (nemůžeš použít v top-level kódu nebo běžných funkcích)
  2. Pozastaví vykonávání funkce, dokud Promise není vyřešen
  3. Vrací hodnotu z Promise (ne Promise samotný!)

Syntaxe:

const hodnota = await promise;

Co se stane:

1. await promise
   
2. Funkce se POZASTAVÍ (pause)
   
3. Čeká,  se promise vyřeší
   
4. Pokračuje s hodnotou z promise

Příklad:

async function example() {
  console.log('Start');

  const result = await Promise.resolve(42);
  // Program se tu ZASTAVÍ a čeká na Promise

  console.log('Result:', result);  // 42
  console.log('End');
}

example();
console.log('After call');

// Výstup:
// Start
// After call
// Result: 42
// End

Co se stalo?

  1. example() se spustila → vypsal "Start"
  2. Narazila na await → pozastavila se
  3. Vrátila se kontrola → vypsal se "After call"
  4. Promise se vyřešil → example() pokračovala
  5. Vypsal se "Result: 42" a "End"

Async/Await vs Promises

Stejný kód, dva způsoby zápisu:

S Promises:

function loadUserData(userId) {
  return fetchUser(userId)
    .then(user => {
      return fetchPosts(user.id);
    })
    .then(posts => {
      return fetchComments(posts[0].id);
    })
    .then(comments => {
      return comments;
    });
}

S Async/Await:

async function loadUserData(userId) {
  const user = await fetchUser(userId);
  const posts = await fetchPosts(user.id);
  const comments = await fetchComments(posts[0].id);
  return comments;
}

Výhody async/await:

  • ✅ Čitelnější - vypadá jako synchronní kód
  • ✅ Lineární - čte se shora dolů
  • ✅ Méně syntaxe - žádné .then()
  • ✅ Snadnější debugging - call stack je přehlednější
  • ✅ Error handling s try/catch (viz další lekce)

Sekvenční vs Paralelní běh

POZOR! await pozastaví funkci → kroky běží sekvenčně (jeden po druhém).

❌ Sekvenčně (POMALÉ):

async function loadData() {
  const user = await fetchUser();      // Čeká 1s
  const posts = await fetchPosts();    // Čeká dalších 1s
  const comments = await fetchComments(); // Čeká dalších 1s

  // Celkem: 3 sekundy!
}

✅ Paralelně (RYCHLÉ):

async function loadData() {
  // Spusť všechny najednou
  const userPromise = fetchUser();
  const postsPromise = fetchPosts();
  const commentsPromise = fetchComments();

  // Počkej na všechny
  const user = await userPromise;
  const posts = await postsPromise;
  const comments = await commentsPromise;

  // Nebo lépe s Promise.all():
  const [user2, posts2, comments2] = await Promise.all([
    fetchUser(),
    fetchPosts(),
    fetchComments()
  ]);

  // Celkem: 1 sekunda (nejdelší)!
}

Return value z async funkce

Async funkce VŽDY vrací Promise!

async function getValue() {
  return 42;  // Vrátíš hodnotu
}

const result = getValue();
console.log(result);  // Promise { <fulfilled>: 42 }

// Použij .then() nebo await:
getValue().then(value => console.log(value));  // 42

// Nebo:
const value = await getValue();  // 42 (v async funkci)

Error handling

Pokud Promise selže → await vyhodí chybu!

async function example() {
  try {
    const result = await Promise.reject('Chyba!');
    console.log('Nikdy se nedostaneš sem');
  } catch (error) {
    console.error('Chytil jsem chybu:', error);  // 'Chyba!'
  }
}

(Detailní error handling bude v další lekci 7.9)

Klíčové koncepty

  • async funkce - Vždy vrací Promise, může používat await
  • await výraz - Pozastaví funkci, dokud Promise není vyřešen, vrací hodnotu
  • Syntactic sugar - Pod kapotou jsou stále Promises
  • Synchronní vzhled - Vypadá jako synchronní kód, ale je async
  • Sekvenční await - Každý await čeká → pomalé
  • Paralelní běh - Použij Promise.all() pro paralelizaci

JavaScript

Základní async funkce

// Async funkce VŽDY vrací Promise
async function greet() {
  return 'Hello!';  // Automaticky Promise.resolve('Hello!')
}

greet().then(msg => console.log(msg));  // 'Hello!'

// Ekvivalentní s:
function greet() {
  return Promise.resolve('Hello!');
}

Použití await

async function fetchData() {
  console.log('Začínám...');

  // Simulace async operace
  const data = await new Promise((resolve) => {
    setTimeout(() => resolve('Data načtena'), 1000);
  });

  console.log(data);  // 'Data načtena' (po 1s)
  return data;
}

fetchData().then(result => {
  console.log('Hotovo:', result);
});

// Výstup:
// Začínám...
// (po 1s): Data načtena
// Hotovo: Data načtena

Co se stalo?

  1. fetchData() se spustila → "Začínám..."
  2. await pozastavil funkci na 1 sekundu
  3. Promise se vyřešil → "Data načtena"
  4. return data → Promise.resolve('Data načtena')
  5. .then() dostal hodnotu → "Hotovo: Data načtena"

Sekvenční async operace

async function loadUserData(userId) {
  console.log('Loading user...');
  const user = await fetch(`/api/users/${userId}`).then(r => r.json());
  console.log('User loaded:', user.name);

  console.log('Loading posts...');
  const posts = await fetch(`/api/posts?userId=${userId}`).then(r => r.json());
  console.log('Posts loaded:', posts.length);

  console.log('Loading comments...');
  const comments = await fetch(`/api/comments?postId=${posts[0].id}`).then(r => r.json());
  console.log('Comments loaded:', comments.length);

  return { user, posts, comments };
}

// Použití
loadUserData(1).then(data => {
  console.log('All data:', data);
});

Paralelní běh s Promise.all()

async function loadAllData() {
  console.log('Starting parallel fetch...');

  // Spusť všechny najednou
  const [users, posts, comments] = await Promise.all([
    fetch('/api/users').then(r => r.json()),
    fetch('/api/posts').then(r => r.json()),
    fetch('/api/comments').then(r => r.json())
  ]);

  console.log('All loaded!');
  return { users, posts, comments };
}

loadAllData().then(data => {
  console.log('Users:', data.users.length);
  console.log('Posts:', data.posts.length);
  console.log('Comments:', data.comments.length);
});

Await v loop

// ❌ Sekvenčně - POMALÉ
async function processFilesSlow(files) {
  const results = [];

  for (const file of files) {
    const content = await readFile(file);  // Čeká na každý soubor!
    results.push(content);
  }

  return results;
}

// ✅ Paralelně - RYCHLÉ
async function processFilesFast(files) {
  const promises = files.map(file => readFile(file));
  const results = await Promise.all(promises);
  return results;
}

Async arrow funkce

// Async arrow function
const fetchData = async () => {
  const data = await fetch('/api/data');
  return data.json();
};

// Použití
fetchData().then(data => console.log(data));

// Async arrow v .map()
const userIds = [1, 2, 3];

Promise.all(
  userIds.map(async (id) => {
    const user = await fetch(`/api/users/${id}`);
    return user.json();
  })
).then(users => console.log(users));

Return Promise z async funkce

async function fetchUser(id) {
  // Můžeš vrátit Promise přímo
  return fetch(`/api/users/${id}`).then(r => r.json());

  // Je to ekvivalentní s:
  // const response = await fetch(`/api/users/${id}`);
  // const data = await response.json();
  // return data;
}

Async IIFE (Immediately Invoked Function Expression)

// Top-level await není podporován všude, použij async IIFE
(async () => {
  const data = await fetchData();
  console.log(data);
})();

// Nebo s error handling:
(async () => {
  try {
    const data = await fetchData();
    console.log(data);
  } catch (error) {
    console.error(error);
  }
})();

Timeout s async/await

function timeout(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}

async function delayedGreeting() {
  console.log('Hello');
  await timeout(1000);
  console.log('World');  // Po 1 sekundě
}

delayedGreeting();

// Výstup:
// Hello
// (po 1s): World

TypeScript

TypeScript přidává typovou bezpečnost pro async funkce a jejich návratové hodnoty.

Typované async funkce

// Explicitní návratový typ: Promise<string>
async function fetchMessage(): Promise<string> {
  const response = await fetch('/api/message');
  const data = await response.json();
  return data.message;  // TS ví, že musí být string
}

// TS automaticky odvodí Promise<number>
async function calculate() {
  return 42;  // TS odvodí: Promise<number>
}

Await s typovanými Promises

interface User {
  id: number;
  name: string;
  email: string;
}

async function fetchUser(id: number): Promise<User> {
  const response = await fetch(`/api/users/${id}`);
  const user: User = await response.json();
  return user;
}

// Použití
async function main() {
  const user = await fetchUser(1);
  // TS ví, že user: User
  console.log(user.name);        // ✅ OK
  // console.log(user.phone);    // ❌ Error - User nemá phone
}

Generické async funkce

// Generická async funkce
async function fetchData<T>(url: string): Promise<T> {
  const response = await fetch(url);
  const data: T = await response.json();
  return data;
}

// Použití
interface Post {
  id: number;
  title: string;
}

const post = await fetchData<Post>('/api/posts/1');
console.log(post.title);  // TS ví, že je to string

Error handling s typy

class ApiError extends Error {
  constructor(public statusCode: number, message: string) {
    super(message);
  }
}

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

    if (!response.ok) {
      throw new ApiError(response.status, 'Failed to fetch user');
    }

    const user: User = await response.json();
    return user;
  } catch (error) {
    if (error instanceof ApiError) {
      console.error('API Error:', error.statusCode, error.message);
    }
    throw error;
  }
}

Promise.all s typy

interface User {
  id: number;
  name: string;
}

interface Post {
  id: number;
  title: string;
}

async function loadAllData(): Promise<{ users: User[], posts: Post[] }> {
  const [users, posts] = await Promise.all([
    fetch('/api/users').then(r => r.json() as Promise<User[]>),
    fetch('/api/posts').then(r => r.json() as Promise<Post[]>)
  ]);

  // TS ví typy
  return { users, posts };
}

Async metody v class

class UserService {
  async getUser(id: number): Promise<User> {
    const response = await fetch(`/api/users/${id}`);
    return response.json();
  }

  async getAllUsers(): Promise<User[]> {
    const response = await fetch('/api/users');
    return response.json();
  }

  async createUser(data: Omit<User, 'id'>): Promise<User> {
    const response = await fetch('/api/users', {
      method: 'POST',
      body: JSON.stringify(data)
    });
    return response.json();
  }
}

// Použití
const service = new UserService();
const user = await service.getUser(1);
console.log(user.name);  // TS ví typ

Rozdíl JS vs TS

JavaScript:

  • Async/await bez typů
  • Nevíš, co await vrátí
  • Chyby až za běhu
  • Žádná kontrola návratového typu

TypeScript:

  • async function(): Promise - přesný typ návratové hodnoty
  • TS ví, co await vrátí
  • Kontrola typů při kompilaci
  • Autocomplete v IDE

Příklad:

// JS - nevíš co dostaneš
async function fetchData() {
  const data = await fetch('/api/data');
  return data.json();  // Co to vrací?
}

// TS - víš přesně
async function fetchData(): Promise<User> {
  const response = await fetch('/api/data');
  const user: User = await response.json();
  return user;  // ✅ TS ví, že je to User
}

Tip

💡 Async funkce vždy vrací Promise:

async function getValue() {
  return 42;
}

// ❌ NELZE takhle
const value = getValue();
console.log(value);  // Promise, ne 42!

// ✅ Použij await nebo .then()
const value = await getValue();  // 42 (v async funkci)
// Nebo:
getValue().then(v => console.log(v));  // 42

💡 Sekvenční vs Paralelní:

// ❌ Sekvenčně - 3 sekundy
async function slow() {
  const a = await fetchA();  // 1s
  const b = await fetchB();  // 1s
  const c = await fetchC();  // 1s
}

// ✅ Paralelně - 1 sekunda
async function fast() {
  const [a, b, c] = await Promise.all([
    fetchA(),  // Všechny
    fetchB(),  // běží
    fetchC()   // najednou!
  ]);
}

💡 Top-level await (ES2022):

// ❌ Nefunguje v starších prostředích
const data = await fetchData();  // Error!

// ✅ Použij async IIFE
(async () => {
  const data = await fetchData();
  console.log(data);
})();

💡 Await v loop - POZOR!:

// ❌ POMALÉ - sekvenčně
for (const id of [1, 2, 3]) {
  const user = await fetchUser(id);  // Čeká na každého!
}

// ✅ RYCHLÉ - paralelně
const users = await Promise.all(
  [1, 2, 3].map(id => fetchUser(id))
);

💡 Error handling:

// ✅ VŽDY obalit v try/catch (nebo .catch())
async function fetchData() {
  try {
    const data = await fetch('/api/data');
    return data.json();
  } catch (error) {
    console.error('Error:', error);
    throw error;  // Re-throw pokud chceš
  }
}

// Nebo:
fetchData().catch(error => console.error(error));

💡 Await !== blokovací:

async function example() {
  console.log('Start');
  await timeout(1000);
  console.log('After await');
}

example();
console.log('After call');

// Výstup:
// Start
// After call  ← Nevyčkalo!
// (po 1s): After await

Kvíz

Které výroky o async/await jsou pravdivé?

- Async funkce VŽDY vrací Promise - i když return hodnotu, automaticky se zabalí do Promise.resolve()

- await lze použít POUZE uvnitř async funkcí (nebo v top-level await v ES2022 modulech)

- await pozastaví pouze async funkci, NE celý program! Ostatní kód může běžet dál (event loop pokračuje)

- async/await je syntactic sugar nad Promises - pod kapotou se stále používají Promises

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