Promise i async/await — wynik przyjdzie później
Program nie musi zatrzymywać całej pracy podczas oczekiwania. Przećwicz sukces, błąd i kilka równoległych operacji bez połączenia z internetem.
Po tej lekcji potrafisz
- Odczytasz Promise<T> przez await i then.
- Obsłużysz odrzucenie Promise w catch.
- Dobierzesz wykonanie sekwencyjne lub Promise.all.
Wczytywanie wyników turnieju
W prawdziwej aplikacji dane mogą przyjść z pliku albo serwera. W ćwiczeniach użyjemy lokalnych obietnic i krótkiego timera, żeby każdy uczeń dostał ten sam wynik bez sieci. Promise reprezentuje przyszły rezultat, a nie samą gotową listę.
Małe kroki, konkretne efekty
async zawsze zwraca Promise
Wartość zwrócona przez async zostaje opakowana w obietnicę. await w funkcji async wstrzymuje dalszą część tej funkcji do rozstrzygnięcia obietnicy. Nie blokuje całego programu.
async function punkty(): Promise<number> { return 42; }
async function main(): Promise<void> {
const wynik = await punkty();
console.log("Punkty: " + wynik);
}
main().catch(console.error);
Punkty: 42
main().catch(…) obsługuje odrzucenie głównej funkcji. Samo void main() nie przechwytuje błędów.
then działa po bieżącym kodzie
Obietnica może być już spełniona, ale callback then i tak wykona się po zakończeniu bieżącego synchronicznego fragmentu. Nie próbuj odczytywać przyszłego wyniku z zewnętrznej zmiennej zaraz po wywołaniu.
console.log("A");
Promise.resolve(7).then(n => console.log("B: " + n));
console.log("C");
A
C
B: 7
Promise ma stan oczekiwania, spełnienia lub odrzucenia. Wynik rozstrzygniętej obietnicy nie zmienia się ponownie.
Wyniki z symulowanego źródła
Timer symuluje krótkie oczekiwanie. Wariant false zgłasza problem połączenia. Nie pobieramy żadnych danych z internetu.
function pobierz(dostepne: boolean): Promise<number[]> {
return new Promise((resolve, reject) => {
setTimeout(() => {
if (dostepne) { resolve([10, 20, 30]); }
else { reject(new Error("Źródło niedostępne")); }
}, 10);
});
}
async function raport(ok: boolean): Promise<void> {
try {
const wyniki = await pobierz(ok);
console.log("Suma: " + wyniki.reduce((s, n) => s + n, 0));
} catch (e) {
console.log(e instanceof Error ? e.message : "Nieznany błąd");
}
}
async function main(): Promise<void> {
await raport(true);
await raport(false);
}
main().catch(console.error);
Suma: 60
Źródło niedostępne
Jak program dochodzi do wyniku
- new Promise otrzymuje funkcję z resolve i reject. To narzędzia do rozstrzygnięcia obietnicy.
- setTimeout planuje callback; 10 ms to próg, nie gwarantowany dokładny czas wykonania.
- await sprawia, że odrzucenie trafia do otaczającego catch.
- Dwa await w main gwarantują kolejność raportów.
Zanim przejdziesz dalej
Czy try { pobierz(false); } catch (…) przejmie późniejsze odrzucenie?
Sprawdź swoje rozumowanie
Nie. Trzeba użyć await wewnątrz try albo dołączyć catch do zwróconej obietnicy.
Zmień kod i zobacz, co się stanie
Kilka niezależnych wyników
Promise.all zbiera wyniki w kolejności wejściowych obietnic, nie kolejności ich zakończenia. Odrzuci się po odrzuceniu jednej; pozostałych operacji automatycznie nie anuluje.
async function main(): Promise<void> {
const [a, b] = await Promise.all([Promise.resolve(10), Promise.resolve(20)]);
console.log(a + b);
const wyniki = await Promise.allSettled([Promise.resolve("OK"), Promise.reject(new Error("Brak"))]);
for (const w of wyniki) { console.log(w.status); }
}
main().catch(console.error);
30
fulfilled
rejected
allSettled czeka na wszystkie wyniki i opisuje każdy statusem. forEach z async nie czeka na callbacki. Do kolejności użyj for…of z await, a do niezależnych operacji map i Promise.all.
Gdy wynik zaskakuje
| Pomyłka | Co się dzieje i jak to poprawić |
|---|---|
| Promise potraktowane jak gotowe dane | Najpierw await lub then. |
| Brak await wewnątrz try | Późniejsze odrzucenie nie zostanie przejęte przez taki catch. |
| Asynchroniczność utożsamiona z osobnym wątkiem | Promise i async same nie przenoszą obliczeń do innego wątku. Długa synchroniczna pętla nadal blokuje. |
Teraz Twoja kolej
Rozwiązanie jest jedną z możliwości. Zanim je odkryjesz, uruchom własną wersję dla podanych danych i przynajmniej jednego przypadku brzegowego.
Funkcja async nagroda zwraca 100. Odczytaj wynik w async main i wypisz go.
Mała podpowiedź
Typ wyniku nagrody to Promise<number>.
Przykładowe rozwiązanie z wyjaśnieniem
async function nagroda(): Promise<number> { return 100; }
async function main(): Promise<void> { console.log(await nagroda()); }
main().catch(console.error);
100
await daje liczbę znajdującą się w spełnionej obietnicy.
Zapisz i sprawdź kolejność: „Start”, callback then z „Wynik”, „Dalej”.
Mała podpowiedź
then nie wykona callbacku w środku bieżącego fragmentu.
Przykładowe rozwiązanie z wyjaśnieniem
console.log("Start");
Promise.resolve().then(() => console.log("Wynik"));
console.log("Dalej");
Start
Dalej
Wynik
To podstawowe ćwiczenie rozumienia kolejności, bez zależności od długości timerów.
Funkcja async ma rzucić Error(„Serwer zajęty”). Odbiorca powinien wypisać ten komunikat przez catch.
Mała podpowiedź
throw wewnątrz async odrzuca zwróconą obietnicę.
Przykładowe rozwiązanie z wyjaśnieniem
async function pobierz(): Promise<number> { throw new Error("Serwer zajęty"); }
async function main(): Promise<void> {
try { console.log(await pobierz()); }
catch (e) { if (e instanceof Error) { console.log(e.message); } }
}
main().catch(console.error);
Serwer zajęty
Obsługujemy błąd w miejscu, które wie, jak go pokazać.
Dla id [1, 2, 3] wywołaj async nick zwracającą „Gracz ” + id. Zbierz wyniki przez map i Promise.all, wypisz w jednej linii.
Mała podpowiedź
map tworzy tablicę Promise<string>.
Przykładowe rozwiązanie z wyjaśnieniem
async function nick(id: number): Promise<string> { return "Gracz " + id; }
async function main(): Promise<void> {
const nicki = await Promise.all([1, 2, 3].map(id => nick(id)));
console.log(nicki.join(", "));
}
main().catch(console.error);
Gracz 1, Gracz 2, Gracz 3
Wynik zachowuje kolejność id. all nie oznacza automatycznie osobnych wątków.
Co zabierasz do następnej lekcji
- Promise<T> reprezentuje przyszły wynik typu T.
- await ułatwia zapis kolejności i obsługi odrzuceń.
- Promise.all i allSettled służą różnym oczekiwaniom wobec błędów.