07.07 Promise.race
Výuka
Proč Promise.race?
Promise.all() čeká na VŠECHNY Promises. Ale co když potřebuješ jen PRVNÍ výsledek?
Příklady kdy potřebuješ "první, kdo doběhne":
- Timeout - "Zkus načíst data, ale max 3 sekundy"
- Fallback - "Zkus primární API, pokud je pomalé, použij záložní"
- Fastest server - "Pošli request na 3 servery, použij nejrychlejší odpověď"
- User cancellation - "Čekej na data, dokud user neklikne Cancel"
Promise.race() = Závod (race) - první, kdo dojde do cíle, vyhrává!
Představ si to jako:
Máš 3 donášky jídla:
- Uber Eats
- Wolt
- Bolt Food
Objednáš VŠECHNY TŘI najednou a čekáš, KDO DORAZÍ PRVNÍ.
Jakmile první dorazí → bereš jídlo, ostatní dvě donášky IGNORUJEŠ.
Promise.race([
uberEats(), // 15 minut
wolt(), // 10 minut ← VYHRÁVÁ!
boltFood() // 20 minut
])
.then((food) => {
console.log('Jídlo od:', food); // 'Jídlo od: Wolt'
// Wolt dorazil první → vyhrává
// Uber a Bolt se ignorují (i když pak dorazí)
});
Jak Promise.race() funguje?
Signatura:
Promise.race([promise1, promise2, promise3, ...])
.then((firstResult) => {
// firstResult = výsledek PRVNÍHO doběhlého Promise
});
Pravidla:
- Vstup: Pole Promises
- Spustí všechny paralelně (jako
Promise.all()) - Čeká na PRVNÍ, který skončí (fulfilled nebo rejected!)
- Výstup: Promise, který se vyřeší s výsledkem prvního dokončeného Promise
Důležité vlastnosti:
✅ Paralelní běh - Všechny Promises běží současně
✅ First wins - První dokončený (fulfilled ČI rejected) vyhrává
✅ Ignoruje ostatní - Ostatní Promises se dokončí, ale jejich výsledky se ignorují
⚠️ Prázdné pole - Promise.race([]) NIKDY neskončí - visí napořád! ⚠️
Srovnání s Promise.all():
| Promise.all() | Promise.race() |
|---|---|
| Čeká na VŠECHNY | Čeká na PRVNÍ |
| Vrací pole výsledků | Vrací jeden výsledek |
| Fail-fast (první chyba) | Fail-fast (první cokoliv) |
Prázdné pole → resolved [] |
Prázdné pole → NIKDY ⚠️ |
| "Gate" pattern | "Latch" pattern |
Schéma:
Promise.race([p1, p2, p3])
│
├─ p1 (5s) ───────────┐
├─ p2 (2s) ────┐ │ Běží paralelně
└─ p3 (10s) ─────────────┐
│ │ │ │
↓ (p2 skončí první!) │ │
│ ↓ │ │
Promise<result2> ✓ (ignoruje) (ignoruje)
Latch pattern (Západka)
Promise.race() implementuje "latch" pattern:
Představ si západku na dveřích:
- První, kdo přijde → otevře západku → dveře se otevřou
- Ostatní → dveře už jsou otevřené → jejich klíč se ignoruje
Promise.race([
slowPromise(), // 10s
fastPromise(), // 1s ← První otevře "západku"
slowPromise2() // 5s
])
.then(() => {
console.log('Dveře otevřené!');
// fastPromise otevřel západku
// slowPromise a slowPromise2 se ignorují
});
Fulfillment vs Rejection
Promise.race() reaguje na PRVNÍ vyřešení - ať je to úspěch nebo chyba!
Promise.race([
Promise.reject('Chyba!'), // Skončí první → VYHRÁVÁ!
Promise.resolve('Úspěch') // Skončí druhý → ignoruje se
])
.catch((error) => {
console.error(error); // 'Chyba!'
// První Promise byl rejected → celý race() rejected
});
Metafora:
Závodí 3 běžci:
Běžec 1 spadne po 100m → "SELHAL PRVNÍ" → VYHRÁVÁ (jako rejected)
Běžec 2 doběhne po 200m → ignoruje se
Běžec 3 doběhne po 300m → ignoruje se
Promise.race() hlásí: "Běžec 1 vyhrál (tím, že spadl první)"
Timeout pattern - Nejčastější použití!
Klasický příklad: "Zkus načíst data, ale max 3 sekundy"
function timeout(ms) {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error('Timeout!')), ms);
});
}
Promise.race([
fetchData(), // Může trvat dlouho
timeout(3000) // Max 3 sekundy
])
.then((data) => {
console.log('Data načtena včas!', data);
})
.catch((error) => {
console.error('Timeout nebo chyba:', error.message);
});
Co se stalo?
fetchData()atimeout(3000)běží paralelně- Pokud
fetchData()doběhne do 3s → fulfilled → data se zpracují - Pokud
timeoutdoběhne první → rejected → chyba se zachytí
Kdy použít Promise.race()?
✅ Použij Promise.race() když:
- Potřebuješ první výsledek (rychlost je priorita)
- Implementuješ timeout pro async operace
- Potřebuješ fallback (záložní řešení)
- Čekáš na user akci (např. Cancel button)
- Soutěžíš mezi více zdroji (např. různé API)
❌ Nepoužívej když:
- Potřebuješ všechny výsledky (použij
Promise.all()) - Záleží ti na pořadí dokončení (ne jen na prvním)
- Prázdné pole (nikdy neskončí!)
Klíčové koncepty
- Latch pattern - První otevře západku, ostatní se ignorují
- First wins - První dokončený (fulfilled ČI rejected) vyhrává
- Timeout pattern - Nejčastější use case (race mezi operací a timeoutem)
- Paralelní běh - Všechny Promises běží současně
- Ignoruje ostatní - Ostatní výsledky se zahodí (ale Promises se dokončí!)
JavaScript
Základní použití Promise.race()
// Tři Promises s různými časy
function delay(ms, value) {
return new Promise((resolve) => {
setTimeout(() => resolve(value), ms);
});
}
Promise.race([
delay(3000, 'Pomalý'),
delay(1000, 'Rychlý'), // ← VYHRÁVÁ!
delay(5000, 'Nejpomalejší')
])
.then((result) => {
console.log('Vyhrál:', result); // 'Vyhrál: Rychlý'
});
// Výstup (po 1s):
// Vyhrál: Rychlý
Co se stalo?
- Všechny tři Promises běžely paralelně
delay(1000, 'Rychlý')skončil první → vyhrál- Ostatní dva se dokončily později, ale jejich výsledky se ignorovaly
Rejection vs Fulfillment
Promise.race([
new Promise((_, reject) => setTimeout(() => reject('Chyba!'), 100)),
new Promise(resolve => setTimeout(() => resolve('OK'), 200))
])
.then((result) => {
console.log('Nikdy se nedostaneš sem');
})
.catch((error) => {
console.error('První skončil s chybou:', error); // 'První skončil s chybou: Chyba!'
});
// Výstup (po 100ms):
// První skončil s chybou: Chyba!
Co se stalo?
- První Promise (rejected) skončil po 100ms → vyhrál
- Druhý Promise (fulfilled) skončil po 200ms → ignoroval se
Timeout pattern
function fetchData() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Data načtena');
}, 5000); // Trvá 5 sekund
});
}
function timeout(ms) {
return new Promise((_, reject) => {
setTimeout(() => {
reject(new Error(`Timeout po ${ms}ms`));
}, ms);
});
}
// Timeout 3 sekundy
Promise.race([
fetchData(),
timeout(3000)
])
.then((data) => {
console.log('Úspěch:', data);
})
.catch((error) => {
console.error('Chyba:', error.message); // 'Chyba: Timeout po 3000ms'
});
// Výstup (po 3s):
// Chyba: Timeout po 3000ms
Co se stalo?
fetchData()trvá 5stimeout(3000)skončí po 3s → vyhraje → rejectedfetchData()se dokončí po 5s, ale výsledek se ignoruje
Timeout pattern - Úspěch
// Tentokrát data dorazí včas
function fetchDataFast() {
return new Promise((resolve) => {
setTimeout(() => {
resolve('Data načtena rychle');
}, 1000); // Trvá 1 sekundu
});
}
Promise.race([
fetchDataFast(),
timeout(3000)
])
.then((data) => {
console.log('Úspěch:', data); // 'Úspěch: Data načtena rychle'
})
.catch((error) => {
console.error('Timeout:', error.message);
});
// Výstup (po 1s):
// Úspěch: Data načtena rychle
Co se stalo?
fetchDataFast()skončil po 1s → vyhrál → fulfilledtimeout(3000)skončí po 3s, ale ignoruje se
Fallback pattern
// Zkus primární API, pokud je pomalé, použij fallback
function fetchFromPrimaryAPI() {
return new Promise((resolve) => {
setTimeout(() => resolve('Data z primárního API'), 5000);
});
}
function fetchFromFallbackAPI() {
return new Promise((resolve) => {
setTimeout(() => resolve('Data ze záložního API'), 1000);
});
}
Promise.race([
fetchFromPrimaryAPI(),
fetchFromFallbackAPI() // Rychlejší → vyhrává
])
.then((data) => {
console.log('Dostali jsme:', data); // 'Dostali jsme: Data ze záložního API'
});
// Výstup (po 1s):
// Dostali jsme: Data ze záložního API
Prázdné pole - POZOR!
// ⚠️ NIKDY NESKONČÍ!
Promise.race([])
.then(() => {
console.log('Nikdy se nevypíše');
});
// ... visí napořád ...
Proč?
- Žádný Promise → nikdo nemůže "vyhrát"
- Promise zůstane Pending napořád
- VŽDY kontroluj, že pole není prázdné!
Reálný příklad - User cancellation
// User může zrušit operaci kliknutím na tlačítko
function fetchWithCancellation(button) {
// Promise pro fetch
const fetchPromise = fetch('/api/data')
.then(res => res.json());
// Promise pro Cancel button
const cancelPromise = new Promise((_, reject) => {
button.addEventListener('click', () => {
reject(new Error('Zrušeno uživatelem'));
}, { once: true });
});
// Race mezi fetch a cancel
return Promise.race([fetchPromise, cancelPromise]);
}
// Použití
const cancelButton = document.querySelector('#cancel');
fetchWithCancellation(cancelButton)
.then((data) => {
console.log('Data načtena:', data);
})
.catch((error) => {
console.error(error.message); // 'Zrušeno uživatelem'
});
Fastest server
// Pošli request na 3 servery, použij nejrychlejší odpověď
Promise.race([
fetch('https://api1.example.com/data'),
fetch('https://api2.example.com/data'),
fetch('https://api3.example.com/data')
])
.then(response => response.json())
.then(data => {
console.log('Nejrychlejší server odpověděl:', data);
})
.catch(error => {
console.error('První server selhal:', error);
});
TypeScript
TypeScript přidává typovou bezpečnost pro Promise.race().
Typované Promise.race()
// TypeScript odvodí společný typ
Promise.race([
Promise.resolve(42), // Promise<number>
Promise.resolve(100), // Promise<number>
Promise.resolve(5) // Promise<number>
])
.then((num: number) => {
// TS ví, že num je number
console.log(num.toFixed(2)); // ✅
});
TypeScript odvodí typ:
Promise.race([Promise<T1>, Promise<T2>, ...]) → Promise<T1 | T2 | ...>
Různé typy - Union type
// Různé typy → Union type
Promise.race([
Promise.resolve(42), // Promise<number>
Promise.resolve('hello') // Promise<string>
])
.then((result: number | string) => {
// TS ví, že result je number NEBO string
if (typeof result === 'number') {
console.log(result.toFixed(2)); // ✅ V tomto bloku je number
} else {
console.log(result.toUpperCase()); // ✅ V tomto bloku je string
}
});
Timeout pattern s typy
function timeout<T = never>(ms: number): Promise<T> {
return new Promise((_, reject) => {
setTimeout(() => reject(new Error(`Timeout po ${ms}ms`)), ms);
});
}
interface User {
id: number;
name: string;
}
function fetchUser(): Promise<User> {
return new Promise((resolve) => {
setTimeout(() => {
resolve({ id: 1, name: 'Jan' });
}, 2000);
});
}
// TS odvodí Promise<User>
Promise.race([
fetchUser(),
timeout<User>(3000)
])
.then((user: User) => {
console.log(user.name); // ✅ TS ví, že je to User
})
.catch((error: Error) => {
console.error(error.message);
});
Generická funkce s timeout
// Generická funkce pro timeout
function withTimeout<T>(
promise: Promise<T>,
ms: number
): Promise<T> {
return Promise.race([
promise,
timeout<T>(ms)
]);
}
// Použití
withTimeout(fetchUser(), 3000)
.then((user: User) => {
console.log(user.name); // ✅ TS ví typ
});
Error handling s typy
class TimeoutError extends Error {
constructor(ms: number) {
super(`Timeout po ${ms}ms`);
this.name = 'TimeoutError';
}
}
function timeout<T>(ms: number): Promise<T> {
return new Promise((_, reject) => {
setTimeout(() => reject(new TimeoutError(ms)), ms);
});
}
Promise.race([fetchUser(), timeout<User>(3000)])
.catch((error: unknown) => {
if (error instanceof TimeoutError) {
console.error('Timeout!', error.message);
} else if (error instanceof Error) {
console.error('Jiná chyba:', error.message);
}
});
Rozdíl JS vs TS
JavaScript:
Promise.race()bez typů- Nevíš, jaký typ dostaneš v
.then() - Pokud různé typy → musíš kontrolovat za běhu
- Chyby až za běhu
TypeScript:
Promise- union type všech možných výsledků- Autocomplete v IDE
- Type narrowing pomocí
typeofneboinstanceof - Kontrola typů při kompilaci
Příklad:
// JS - nevíš co dostaneš
Promise.race([fetchUser(), fetchPosts()])
.then((result) => {
console.log(result.name); // Může selhat za běhu!
});
// TS - víš možné typy
Promise.race([fetchUser(), fetchPosts()])
.then((result: User | Post[]) => {
if (Array.isArray(result)) {
// TS ví: result je Post[]
console.log(result[0].title);
} else {
// TS ví: result je User
console.log(result.name);
}
});
Tip
💡 Timeout pattern - Helper funkce:
// ✅ Vytvoř reusable helper
function withTimeout(promise, ms) {
const timeoutPromise = new Promise((_, reject) => {
setTimeout(() => reject(new Error('Timeout')), ms);
});
return Promise.race([promise, timeoutPromise]);
}
// Použití
withTimeout(fetchData(), 3000)
.then(handleData)
.catch(handleError);
💡 Promise.race vs Promise.all - Kdy co:
// ❌ NELZE: Kroky závisí na sobě
Promise.race([fetchUser(), fetchSettings()]) // settings potřebuje user!
// ✅ Promise.all: Potřebuješ VŠECHNY výsledky
Promise.all([fetchUser(), fetchPosts()])
// ✅ Promise.race: Potřebuješ PRVNÍ výsledek
Promise.race([primaryAPI(), fallbackAPI()])
💡 Prázdné pole = NIKDY neskončí:
// ⚠️ POZOR!
Promise.race([]) // Visí napořád!
// ✅ Vždy kontroluj
if (promises.length === 0) {
return Promise.reject(new Error('Žádné promises'));
}
return Promise.race(promises);
💡 Fallback cascade (kaskáda záloh):
// Zkus API v pořadí: primární → sekundární → terciární
function fetchWithFallbacks() {
return Promise.race([
fetch('/primary-api'),
delay(1000).then(() => fetch('/secondary-api')),
delay(2000).then(() => fetch('/tertiary-api'))
]);
}
💡 Race vs allSettled:
// Promise.race() - První výsledek (success nebo error)
Promise.race([p1, p2, p3]) // První, kdo skončí
// Promise.allSettled() - VŠECHNY výsledky (i errors)
Promise.allSettled([p1, p2, p3]) // Čeká na všechny, ale neselže
💡 Cleanup pattern:
// Co s Promises, které "prohrály"?
// Odpověď: Dokončí se, ale ignorují se.
// Pokud potřebuješ cleanup:
let cleanup = null;
Promise.race([
new Promise(resolve => {
const timer = setTimeout(() => resolve('OK'), 5000);
cleanup = () => clearTimeout(timer);
}),
timeout(1000)
])
.finally(() => {
if (cleanup) cleanup(); // Vyčisti zdroje
});
Kvíz
Které výroky o Promise.race() jsou pravdivé?
❌ - Promise.race() vrací JEDEN výsledek (prvního, kdo skončil), NE pole! Pole vrací Promise.all().
✅ - First wins - První Promise, který se vyřeší (ať fulfilled nebo rejected), určuje výsledek celého race()
✅ - Pokud první Promise je rejected, celý Promise.race() je okamžitě rejected s tou chybou
❌ - Promise.race([]) NIKDY neskončí - visí v Pending stavu napořád! (Je to bug v ES6 spec, ale zůstalo to kvůli kompatibilitě)
🎯 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ě