Funkcje asynchroniczne są kluczowym elementem nowoczesnych aplikacji, szczególnie w pracy z operacjami, które wymagają pewnego czasu, jak np. zapytania do API czy praca z bazami danych. TypeScript, dzięki swojemu systemowi typów, pozwala precyzyjnie określać, co zwracają funkcje asynchroniczne, co pomaga programistom lepiej zrozumieć, czego mogą się spodziewać po kodzie.
W tym wpisie omówimy, jak poprawnie typować funkcje asynchroniczne w TypeScript, z naciskiem na funkcje zwracające obietnice (Promises).
Co to jest Promise?
Obietnica (Promise) w JavaScript (i TypeScript) to obiekt reprezentujący zakończoną lub trwającą operację asynchroniczną oraz jej wynik. Może zakończyć się sukcesem (z wynikiem) lub niepowodzeniem (z błędem).
Obietnice mają trzy możliwe stany:
Pending (Oczekujący): operacja jeszcze się nie zakończyła.
Fulfilled (Zrealizowany): operacja zakończyła się sukcesem i zwraca wynik.
Rejected (Odrzucony): operacja zakończyła się niepowodzeniem i zwraca błąd.
Przykład funkcji zwracającej obietnicę:
function getData(): Promise<string> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve("Dane pobrane");
}, 1000);
});
}
W powyższym przykładzie funkcja getData zwraca obietnicę, która po 1 sekundzie zwróci string "Dane pobrane".
Typowanie obietnic w TypeScript
W TypeScript możemy precyzyjnie określać typ wartości, którą obietnica zwróci po zakończeniu operacji. Używamy do tego typu generycznego Promise<T>, gdzie T reprezentuje typ zwracanej wartości.
Przykład:
function fetchData(): Promise<number> {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(42);
}, 1000);
});
}
Tutaj funkcja fetchData zwraca obietnicę, która po zakończeniu operacji zwróci wartość typu number. Dzięki takiemu typowaniu, TypeScript będzie wiedział, jakiego typu danych możemy się spodziewać w wyniku spełnienia obietnicy.
Typowanie funkcji asynchronicznych z użyciem async/await
TypeScript w pełni wspiera składnię async/await, która pozwala na bardziej zwięzłe i czytelne zarządzanie obietnicami. Funkcje oznaczone jako async zawsze zwracają obietnicę.
Przykład:
async function getNumber(): Promise<number> {
return 42;
}
Tutaj funkcja getNumber jest oznaczona jako asynchroniczna (async) i zwraca obietnicę, która zwróci wartość typu number.
Typowanie funkcji zwracających różne typy obietnic
W praktyce asynchroniczne funkcje mogą zwracać różne typy wyników, takie jak obiekty, tablice lub inne typy. Możemy dokładnie określić typ obietnicy, którą zwraca funkcja, co zwiększa czytelność i niezawodność kodu.
Przykład z obietnicą zwracającą tablicę:
async function fetchItems(): Promise<string[]> {
return ["item1", "item2", "item3"];
}
Tutaj funkcja fetchItems zwraca obietnicę, która po zakończeniu operacji zwróci tablicę stringów.
Funkcja fetchUser zwraca obietnicę, która zwróci obiekt o strukturze zgodnej z interfejsem User.
Łączenie obietnic o różnych typach
Czasami będziemy pracować z kilkoma obietnicami, które zwracają różne typy wartości. W takim przypadku możemy korzystać z funkcji takich jak Promise.all, które pozwalają łączyć wyniki wielu obietnic, a TypeScript może automatycznie wywnioskować typ wyniku.
Tutaj funkcja fetchData zwraca obietnicę, która po zakończeniu zwróci krotkę zawierającą string i liczbę.
Obsługa błędów w funkcjach asynchronicznych
Gdy pracujemy z obietnicami, musimy obsługiwać potencjalne błędy. W TypeScript możemy używać bloku try...catch do obsługi błędów w funkcjach asynchronicznych.
Przykład:
async function getData(): Promise {
try {
const data = await fetch("https://api.example.com/data");
if (!data.ok) {
throw new Error("Błąd w trakcie pobierania danych");
}
return await data.json();
} catch (error) {
throw new Error(`Nie udało się pobrać danych: ${error}`);
}
}
W tym przypadku funkcja getData próbuje pobrać dane z API, a jeśli coś pójdzie nie tak (np. problem z siecią), rzuca wyjątek. Typowany wynik to Promise, co oznacza, że po zakończeniu obietnicy oczekujemy stringa.
Typowanie funkcji asynchronicznych z obietnicami wielokrotnymi
Możemy także tworzyć funkcje, które zwracają różne typy wyników w zależności od warunków. TypeScript pozwala na dokładne określenie typów dla takich sytuacji.
Przykład:
async function fetchData(flag: boolean): Promise {
if (flag) {
return "Dane pobrane";
} else {
return 42;
}
}
Tutaj funkcja fetchData może zwracać albo string, albo liczbę, w zależności od wartości przekazanego parametru flag. TypeScript dzięki temu zapewnia, że obsługujemy oba możliwe typy wyników.
Typ Promise
Funkcje asynchroniczne nie zawsze muszą zwracać wartość. W TypeScript możemy typować takie funkcje jako Promise<void>, co oznacza, że obietnica nie zwróci żadnej wartości po zakończeniu operacji.
Przykład:
async function logMessage(): Promise {
console.log("Wiadomość zalogowana.");
}
Tutaj funkcja logMessage nie zwraca żadnej wartości, a obietnica zakończy się bez wyniku.
Podsumowanie
Typowanie funkcji asynchronicznych w TypeScript pozwala na bardziej precyzyjne kontrolowanie, jakie wyniki zwracają obietnice, co zwiększa niezawodność kodu. Kluczowe aspekty typowania funkcji asynchronicznych obejmują:
Użycie typu generycznego Promise<T> do określenia typu wartości zwracanej przez obietnicę.
Typowanie funkcji async za pomocą typu Promise.
Łączenie wielu obietnic o różnych typach i obsługa wyników z użyciem np. Promise.all.
Obsługa błędów w funkcjach asynchronicznych z użyciem bloku try...catch.
Tworzenie funkcji zwracających różne typy wyników, co pozwala na bardziej elastyczne podejście do obsługi operacji asynchronicznych.