Menu

Promise w TypeScript: Promise<T>, Promise.all i allSettled

Jak typuje się promise w TypeScript: typ Promise<T>, typowanie new Promise i resolve, jak then zmienia typ, dlaczego catch daje any, Promise.all z wynikami w krotce, typy wyników Promise.allSettled oraz opakowywanie API z callbackami w otypowany promise.

Na tej stronie są działające edytory: edytuj, uruchamiaj i od razu zobacz wynik.

W TypeScript promise ma typ Promise<T>, gdzie T to typ wartości, którą się rozwiązuje. Funkcja zwracająca promise deklaruje to w typie zwracanym, a wartość, którą dostajesz z then lub await, ma typ T.

Mechanika promise'ów (stany, kolejka mikrozadań, łańcuchowanie) to zwykły JavaScript, omówiony w JavaScript promises. Ta strona dotyczy typów.

Typowanie new Promise i resolve

Gdy new Promise jest wartością zwracaną funkcji z zadeklarowanym typem zwracanym, T pochodzi stamtąd. Samodzielnie TypeScript nie wnioskuje T z wywołań resolve i dostajesz Promise<unknown>. Przekaż argument typu jawnie:

Dwa szczegóły. resolve jest typowane na podstawie T, więc resolve("42") w Promise<number> to błąd kompilacji. A resolve() bez argumentu jest dozwolone tylko wtedy, gdy T zawiera void: w Promise<number> to błąd TS2794 (Expected 1 arguments, but got 0. Did you forget to include 'void' in your type argument to 'Promise'?).

Strona reject nie jest typowana. Nie ma Promise<T, E>: promise może zostać odrzucony dowolną wartością.

then, catch i finally

Każde then zwraca nowy promise o typie tego, co zwraca jego callback. Jeśli callback zwraca promise, TypeScript go rozpakowuje, więc nigdy nie dostajesz Promise<Promise<T>>.

Parametr callbacku catch ma w bibliotece standardowej typ any, a nie unknown. Nic nie powstrzyma cię przed napisaniem e.message na wartości, która może być stringiem. Oznacz go jako unknown, jak wyżej, i zawęź przed użyciem. try/catch wokół await radzi sobie lepiej: przy strict jego zmienna catch już jest unknown (zobacz obsługę błędów).

Promise.all zwraca krotkę

Dla literału tablicy z promise'ami różnych typów Promise.all zwraca promise krotki, w której każda pozycja zachowuje własny typ:

Można też wmieszać wartości niebędące promise'ami, przechodzą bez zmian. Promise.all odrzuca, gdy tylko jedno wejście zostanie odrzucone, a pozostałe wyniki przepadają. Promise.race rozwiązuje się lub odrzuca wynikiem pierwszego zakończonego i ma typ unii wejść; Promise.any rozwiązuje się pierwszym sukcesem i odrzuca z AggregateError tylko wtedy, gdy zawiodą wszystkie wejścia.

Promise.allSettled i typ jego wyniku

Promise.allSettled czeka na każde wejście i nigdy nie odrzuca. Każdy wynik to PromiseSettledResult<T>, unia, którą zawężasz polem status:

r.value istnieje dopiero po sprawdzeniu status, bo wariant odrzucony nie ma value. reason to any, z tego samego powodu co w catch. filter używa predykatu typu, żeby przefiltrowana tablica miała typ spełnionych wyników.

Opakowywanie API z callbackami w promise

Starsze API zgłaszają wyniki przez callback, często w stylu Node (err, result) => void. Opakuj je raz w funkcję zwracającą otypowany promise, a reszta kodu może używać await:

W Node util.promisify robi to dla funkcji zgodnych z konwencją (err, result), a wiele wbudowanych modułów ma już wersje oparte na promise'ach (node:fs/promises, node:timers/promises).

Typowe błędy

  • Zwracanie T tam, gdzie sygnatura mówi Promise<T>, lub odwrotnie. Funkcja nie-async zadeklarowana jako Promise<User> musi zwrócić promise; funkcja async zwraca go automatycznie.
  • Zapominanie o obsłudze promise'a. Wywołanie takie jak save(user); bez await, then czy catch kompiluje się bez problemu, a odrzucenie staje się nieobsłużonym odrzuceniem (które domyślnie kończy proces Node). Wyłapuje to reguła typescript-eslint no-floating-promises.
  • Ufanie typowi .catch((e) => ...). Jego e to any. Oznacz go jako unknown.
  • Używanie new Promise wokół czegoś, co już zwraca promise. Po prostu zwróć istniejący promise albo użyj na nim await.

Najczęściej zadawane pytania

Czym jest Promise<T> w TypeScript?

Promise<T> to typ promise'a, który rozwiązuje się wartością typu T. Funkcja zwracająca Promise<string> oddaje promise, którego callback then albo await daje string. Promise rozwiązujący się bez wartości to Promise<void>.

Jak otypować new Promise w TypeScript?

Przekaż argument typu: new Promise<number>((resolve, reject) => ...). Bez niego TypeScript nie wywnioskuje wartości z wywołań resolve i wynikiem jest Promise<unknown>. Dla promise'a, który rozwiązuje się bez wartości, użyj new Promise<void>(resolve => ...), żeby resolve() bez argumentu było dozwolone.

Jakiego typu jest błąd w catch promise'a?

any. Parametr reason callbacku catch ma w bibliotece standardowej typ any, bo rzucić lub odrzucić można cokolwiek. Sam oznacz go jako unknown (.catch((e: unknown) => ...)) i zawęź przez instanceof Error przed użyciem.

Jak Promise.all działa z typami w TypeScript?

Promise.all na literale tablicy zwraca typ krotki z jednym elementem na każde wejście, w kolejności: Promise.all([getUser(), getCount()]) to Promise<[User, number]>, więc destrukturyzacja daje każdej wartości jej własny typ. Na tablicy jednego typu, T[], zwraca Promise<T[]>.

Czym różni się Promise.all od Promise.allSettled?

Promise.all odrzuca, gdy tylko którekolwiek wejście zostanie odrzucone. Promise.allSettled zawsze się rozwiązuje, tablicą obiektów PromiseSettledResult<T>: { status: "fulfilled", value } albo { status: "rejected", reason }. Sprawdź status, żeby zawęzić każdy z nich.

Ilustracja języków programowania w Coddy

Ucz się programowania z Coddy

ZACZNIJ