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

07.06 Promise.all

Výuka

Proč Promise.all?

Dosud jsme viděli sekvenční Promise řetězy - krok po kroku:

fetchUser()
  .then(user => fetchSettings(user.id))   // Čeká na user
  .then(settings => fetchArticles(settings.limit))  // Čeká na settings

Ale co když potřebuješ několik věcí najednou a jsou nezávislé?

Problém:

// ❌ Neefektivní - běží sekvenčně (30s celkem!)
fetchUser()  // 10s
  .then(() => fetchPosts())  // 10s
  .then(() => fetchComments());  // 10s

// Celkem: 10s + 10s + 10s = 30 sekund 😭

Řešení: Promise.all() - spustí je paralelně:

// ✅ Efektivní - běží paralelně (10s celkem!)
Promise.all([
  fetchUser(),     // 10s
  fetchPosts(),    // 10s
  fetchComments()  // 10s
])
.then(([user, posts, comments]) => {
  // Všechny tři hotové najednou!
});

// Celkem: max(10s, 10s, 10s) = 10 sekund! 🚀

Představ si to jako:

Jdeš do fastfoodu a objednáš si 3 věci najednou: burger, hranolky, nápoj.

❌ Sekvenčně (Promise chain):

1. Uvař burger  počkej  hotovo
2. Teď uvař hranolky  počkej  hotovo
3. Teď naplň nápoj  počkej  hotovo
Celkem: 15 minut

✅ Paralelně (Promise.all):

1. Uvař burger  }
2. Uvař hranolky } Všechno najednou!
3. Naplň nápoj  }

Počkáš,  je VŠECHNO hotové  dostaneš tác s CELOU objednávkou
Celkem: 5 minut (doba nejdelší položky)

Promise.all() je "gate" (brána):

  • Brána se otevře AŽ když všechny Promises skončí
  • Nezáleží na pořadí dokončení
  • Dostaneš pole všech výsledků v původním pořadí

Jak Promise.all() funguje?

Signatura:

Promise.all([promise1, promise2, promise3, ...])
  .then((results) => {
    // results = [result1, result2, result3, ...]
  });

Pravidla:

  1. Vstup: Pole Promises (nebo hodnot)
  2. Spustí všechny paralelně (neblokuje se)
  3. Čeká, až VŠECHNY skončí
  4. Výstup: Promise, který se vyřeší s polem výsledků

Důležité vlastnosti:

Paralelní běh - Všechny Promises běží současně ✅ Pořadí výsledků - Pole výsledků má stejné pořadí jako vstupní pole (ne pořadí dokončení!) ✅ Fail-fast - Pokud jakýkoliv Promise selže, Promise.all() okamžitě selže (ostatní se dokončí, ale jejich výsledky se ignorují) ✅ Prázdné pole - Promise.all([]) se okamžitě vyřeší s []

Schéma:

Promise.all([p1, p2, p3])
       
       ├─ p1 ──┐
       ├─ p2 ──┤ Běží paralelně
       └─ p3 ──┘
       
        (počká na VŠECHNY)
       
Promise<[result1, result2, result3]>

Fail-fast chování

Pokud JAKÝKOLIV Promise selže → celý Promise.all() okamžitě selže:

Promise.all([
  Promise.resolve('OK'),        // Uspěje
  Promise.reject('CHYBA!'),     // SELŽE!
  Promise.resolve('Taky OK')    // Uspěje
])
.then((results) => {
  console.log('Nikdy se nedostaneš sem');
})
.catch((error) => {
  console.error(error);  // 'CHYBA!'
  // Dostaneš JEN první chybu!
});

Co se stalo?

  1. p2 selhal → Promise.all() okamžitě rejected
  2. p1 a p3 se dokončily, ale jejich výsledky se zahodily
  3. .catch() dostal pouze chybu z p2

Metafora:

Objednáš burger, hranolky, nápoj.
Burger se podaří 
Hranolky se spálí 
Nápoj se podaří 

 Celá objednávka se zruší! Nedostaneš NIC.

Pořadí výsledků

Výsledky jsou VŽDY ve stejném pořadí jako vstupní pole (i když se Promises dokončí v jiném pořadí):

Promise.all([
  delay(3000, 'Třetí dokončen'),  // Dokončí se poslední
  delay(1000, 'První dokončen'),  // Dokončí se první
  delay(2000, 'Druhý dokončen')   // Dokončí se druhý
])
.then(([first, second, third]) => {
  console.log(first);   // 'Třetí dokončen'
  console.log(second);  // 'První dokončen'
  console.log(third);   // 'Druhý dokončen'

  // Pořadí výsledků = pořadí v poli, NE pořadí dokončení!
});

Kdy použít Promise.all()?

Použij Promise.all() když:

  • Potřebuješ několik věcí najednou
  • Jsou nezávislé (jedna nevyžaduje výsledek druhé)
  • Potřebuješ všechny výsledky
  • Chceš efektivitu (paralelní běh)

Nepoužívej když:

  • Kroky závisí na sobě (použij Promise chain)
  • Potřebuješ jen jeden výsledek (použij Promise.race())
  • Chceš pokračovat i když něco selže (použij Promise.allSettled())

Klíčové koncepty

  • Gate pattern - Brána se otevře až když VŠECHNY Promises skončí
  • Paralelní běh - Všechny Promises běží současně
  • Pole výsledků - Výstup je pole v původním pořadí (ne pořadí dokončení)
  • Fail-fast - První chyba okamžitě odmítne celý Promise.all()
  • All-or-nothing - Buď dostaneš VŠECHNY výsledky, nebo ŽÁDNÝ

JavaScript

Základní použití Promise.all()

// Tři nezávislé async operace
function fetchUser() {
  return new Promise((resolve) => {
    setTimeout(() => resolve({ name: 'Jan', id: 1 }), 1000);
  });
}

function fetchPosts() {
  return new Promise((resolve) => {
    setTimeout(() => resolve(['Post 1', 'Post 2']), 800);
  });
}

function fetchComments() {
  return new Promise((resolve) => {
    setTimeout(() => resolve(['Comment 1', 'Comment 2']), 600);
  });
}

// Spusť všechny paralelně
console.log('Start');
const startTime = Date.now();

Promise.all([
  fetchUser(),
  fetchPosts(),
  fetchComments()
])
.then(([user, posts, comments]) => {
  const duration = Date.now() - startTime;
  console.log('Vše hotovo za:', duration, 'ms');  // ~1000ms (nejdelší)

  console.log('User:', user.name);
  console.log('Posts:', posts.length);
  console.log('Comments:', comments.length);
});

// Výstup (po ~1000ms):
// Vše hotovo za: 1001 ms
// User: Jan
// Posts: 2
// Comments: 2

Co se stalo?

  • Všechny tři Promises běžely paralelně
  • Celková doba: ~1000ms (nejdelší Promise), NE 1000+800+600=2400ms!
  • Destructuring [user, posts, comments] - rozbalení pole

Pořadí výsledků

// Promises se dokončí v různém pořadí
Promise.all([
  new Promise(resolve => setTimeout(() => resolve('A'), 300)),  // Dokončí 2.
  new Promise(resolve => setTimeout(() => resolve('B'), 100)),  // Dokončí 1.
  new Promise(resolve => setTimeout(() => resolve('C'), 200))   // Dokončí 3.
])
.then(([first, second, third]) => {
  console.log(first);   // 'A' (ne 'B'!)
  console.log(second);  // 'B'
  console.log(third);   // 'C'

  // Pořadí výsledků odpovídá pořadí v poli, NE pořadí dokončení!
});

// Výstup:
// A
// B
// C

Fail-fast chování

Promise.all([
  Promise.resolve('Úspěch 1'),
  Promise.reject('Chyba!'),      // První chyba!
  Promise.resolve('Úspěch 2')
])
.then((results) => {
  console.log('Nikdy se nedostaneš sem');
})
.catch((error) => {
  console.error('Zachycená chyba:', error);  // 'Chyba!'
});

// Výstup:
// Zachycená chyba: Chyba!

Co se stalo?

  • Druhý Promise (rejected) způsobil okamžité selhání Promise.all()
  • První a třetí Promise se ignorovaly
  • .catch() dostal pouze první chybu

Vícenásobné chyby

// Co když selže více Promises?
Promise.all([
  Promise.reject('Chyba 1'),     // První chyba
  Promise.reject('Chyba 2'),     // Druhá chyba
  Promise.resolve('OK')
])
.catch((error) => {
  console.error(error);  // 'Chyba 1'

  // Dostaneš JEN první chybu!
  // 'Chyba 2' se ignoruje
});

// Výstup:
// Chyba 1

Prázdné pole

// Prázdné pole → okamžité vyřešení
Promise.all([])
  .then((results) => {
    console.log(results);  // []
    console.log('Okamžitě vyřešeno!');
  });

// Výstup (OKAMŽITĚ):
// []
// Okamžitě vyřešeno!

Promise.all() s hodnotami (ne-Promises)

// Můžeš míchat Promises a hodnoty
Promise.all([
  Promise.resolve(42),
  'hello',                // Není Promise - automaticky se zabalí
  100,                    // Také ne Promise
  Promise.resolve('world')
])
.then(([a, b, c, d]) => {
  console.log(a);  // 42
  console.log(b);  // 'hello'
  console.log(c);  // 100
  console.log(d);  // 'world'
});

Co se stalo?

  • Ne-Promise hodnoty se automaticky zabalí do Promise.resolve(hodnota)
  • Funguje to, ale nedává smysl - Promise.all() je pro async operace!

Reálný příklad - Paralelní HTTP requesty

// Simulace API requestů
function fetchUserData(userId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve({ userId, name: 'Jan Novák' });
    }, 500);
  });
}

function fetchUserPosts(userId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve([
        { id: 1, title: 'Post 1' },
        { id: 2, title: 'Post 2' }
      ]);
    }, 300);
  });
}

function fetchUserFollowers(userId) {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve(['follower1', 'follower2', 'follower3']);
    }, 400);
  });
}

// Načti všechny data paralelně
const userId = 123;

Promise.all([
  fetchUserData(userId),
  fetchUserPosts(userId),
  fetchUserFollowers(userId)
])
.then(([userData, posts, followers]) => {
  console.log('User:', userData.name);
  console.log('Posts count:', posts.length);
  console.log('Followers count:', followers.length);

  // Vše načteno za ~500ms místo ~1200ms!
})
.catch((error) => {
  console.error('Selhalo načtení dat:', error);
});

// Výstup (po ~500ms):
// User: Jan Novák
// Posts count: 2
// Followers count: 3

TypeScript

TypeScript přidává typovou bezpečnost pro výsledky Promise.all().

Typované Promise.all()

// TypeScript automaticky odvodí typy
Promise.all([
  Promise.resolve(42),           // Promise<number>
  Promise.resolve('hello'),      // Promise<string>
  Promise.resolve(true)          // Promise<boolean>
])
.then(([num, str, bool]) => {
  // TS ví, že:
  // num: number
  // str: string
  // bool: boolean

  console.log(num.toFixed(2));        // ✅ OK
  console.log(str.toUpperCase());     // ✅ OK
  console.log(bool ? 'yes' : 'no');   // ✅ OK
});

TypeScript odvodí typ:

Promise.all([...]) → Promise<[T1, T2, T3, ...]>

Typované API requesty

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

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

interface Comment {
  id: number;
  text: string;
}

function fetchUser(): Promise<User> {
  return Promise.resolve({ id: 1, name: 'Jan' });
}

function fetchPosts(): Promise<Post[]> {
  return Promise.resolve([
    { id: 1, title: 'Post 1', content: 'Content 1' }
  ]);
}

function fetchComments(): Promise<Comment[]> {
  return Promise.resolve([
    { id: 1, text: 'Comment 1' }
  ]);
}

// TS odvodí typy v poli výsledků
Promise.all([
  fetchUser(),
  fetchPosts(),
  fetchComments()
])
.then(([user, posts, comments]) => {
  // TS ví, že:
  // user: User
  // posts: Post[]
  // comments: Comment[]

  console.log(user.name);           // ✅ OK
  console.log(posts[0].title);      // ✅ OK
  console.log(comments[0].text);    // ✅ OK

  // console.log(user.email);       // ❌ Error - User nemá email
});

Explicitní typy

// Můžeš explicitně zadat typ
const promises: [Promise<number>, Promise<string>, Promise<boolean>] = [
  Promise.resolve(42),
  Promise.resolve('hello'),
  Promise.resolve(true)
];

Promise.all(promises)
  .then(([num, str, bool]) => {
    // TS ví přesné typy
  });

Error handling s typy

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

Promise.all([
  fetch('/api/user'),
  fetch('/api/posts')
])
.then(([userRes, postsRes]) => {
  // Zpracuj odpovědi
})
.catch((error: unknown) => {
  // TS vynutí kontrolu typu
  if (error instanceof ApiError) {
    console.error('API Error:', error.code, error.message);
  } else if (error instanceof Error) {
    console.error('Error:', error.message);
  } else {
    console.error('Unknown error');
  }
});

Generická funkce s Promise.all()

// Generická funkce pro paralelní fetching
async function fetchAll<T extends any[]>(
  ...fetchers: { [K in keyof T]: () => Promise<T[K]> }
): Promise<T> {
  return Promise.all(fetchers.map(f => f())) as Promise<T>;
}

// Použití
fetchAll(
  () => fetch('/api/user').then(r => r.json()),
  () => fetch('/api/posts').then(r => r.json())
).then(([user, posts]) => {
  // TS odvodí typy
});

Rozdíl JS vs TS

JavaScript:

  • Promise.all() bez typů
  • Nevíš, jaké typy dostaneš v poli výsledků
  • Musíš si pamatovat pořadí Promises
  • Chyby až za běhu

TypeScript:

  • Promise<[T1, T2, T3, ...]> - přesné typy pro každý výsledek
  • Autocomplete v IDE pro výsledky
  • Kontrola typů při kompilaci
  • Ví pořadí a typy všech výsledků

Příklad:

// JS - nevíš co dostaneš
Promise.all([fetchUser(), fetchPosts()])
  .then(([user, posts]) => {
    console.log(user.name);  // Může selhat za běhu
  });

// TS - víš přesně
Promise.all([fetchUser(), fetchPosts()])
  .then(([user, posts]) => {
    // TS ví: user: User, posts: Post[]
    console.log(user.name);         // ✅ TS ví, že má name
    // console.log(user.email);     // ❌ Error - nemá email
  });

Tip

💡 Destructuring pro čitelnost:

// ❌ Těžko čitelné
Promise.all([fetchUser(), fetchPosts(), fetchComments()])
  .then((results) => {
    const user = results[0];
    const posts = results[1];
    const comments = results[2];
    // ...
  });

// ✅ Čitelné s destructuring
Promise.all([fetchUser(), fetchPosts(), fetchComments()])
  .then(([user, posts, comments]) => {
    // Jasné názvy hned!
  });

💡 Paralelně vs Sekvenčně:

// ❌ Sekvenčně - POMALÉ (30s)
fetchUser()
  .then(() => fetchPosts())      // Čeká 10s
  .then(() => fetchComments());  // Čeká dalších 10s

// ✅ Paralelně - RYCHLÉ (10s)
Promise.all([
  fetchUser(),     // Všechny běží
  fetchPosts(),    // současně!
  fetchComments()
]);

💡 Kdy NEPOUŽÍVAT Promise.all():

// ❌ Kroky závisí na sobě - NELZE paralelizovat!
Promise.all([
  fetchUser(),
  fetchSettings(user.id),  // Potřebuje user! Ale user ještě není!
  fetchArticles(settings.limit)  // Potřebuje settings!
]);

// ✅ Použij Promise chain
fetchUser()
  .then(user => fetchSettings(user.id))
  .then(settings => fetchArticles(settings.limit));

💡 Fail-fast → Vždy .catch():

// ✅ Vždy přidej .catch()
Promise.all([p1, p2, p3])
  .then(handleSuccess)
  .catch(handleError);  // POVINNÉ! Jinak se chyba "polkne"

💡 Prázdné pole = okamžité vyřešení:

Promise.all([]).then(() => {
  console.log('Okamžitě!');
});

// Ekvivalentní s:
Promise.resolve([]).then(() => {
  console.log('Okamžitě!');
});

💡 Pokud potřebuješ pokračovat i při chybách:

// Promise.all() selže na první chybě
// Pokud chceš pokračovat i když něco selže:

// ✅ ES2020: Promise.allSettled()
Promise.allSettled([p1, p2, p3])
  .then((results) => {
    // results = [
    //   { status: 'fulfilled', value: ... },
    //   { status: 'rejected', reason: ... },
    //   ...
    // ]
  });

Kvíz

Které výroky o Promise.all() jsou pravdivé?

- Promise.all() spustí všechny Promises současně (paralelně) - nečeká na dokončení jednoho před spuštěním dalšího

- Výsledky jsou v původním pořadí z pole, NE v pořadí dokončení! Promise.all([p1, p2, p3]) vrátí [result1, result2, result3] bez ohledu na to, který se dokončil první.

- Fail-fast - první chyba způsobí okamžité selhání celého Promise.all(), ostatní výsledky se zahodí

- Promise.all([]) se okamžitě vyřeší s [] (prázdné pole)

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