Promise w TypeScript – rozwiązywanie asynchronicznych funkcji

Czym jest Promise? Jest to obiekt zwracany z metody, dzięki któremu możemy w łatwy sposób obsługiwać asynchroniczne funkcje i ich rezultaty (tak te wynikające z pełnego sukcesu, jak i z błędów, które mogą się zdarzyć w trakcie wykonania).

Przykłady w tym wpisie będą w TypeScript. Aby móc je prawidłowo uruchamiać zapoznaj się z artykułem: TypeScript: Szybki start.

Jak działa Promise?

Promise (ang. obietnica) ma 2 zasadnicze stany:

  • pending – oczekiwanie
  • settled – rozstrzygnięty
    • resolved – rozwiązany (choć spotykałem też określenie “fulfilled”)
    • rejected – odrzucony

Promise’ów używa się w funkcjach asynchronicznych (np. komunikacji ajaksem z serwerem). Dopóki nie mamy odpowiedzi (promise nie jest rozstrzygnięty) uznajemy, że jest w stanie pending (ang. w oczekiwaniu).

Kiedy już mamy wynik działania funkcji może ona być albo zakończona pozytywnie albo nie.

Promise rozwiązany pozytywnie

Promise.resolve('Ta wartość zostanie przekazana do then')
    .then(result => {
        console.log(result);
    });

Jak widać, tworzymy obiekt rozwiązany obiekt Promise za pomocą statycznej metody Promise.resolve.

Ponieważ wiemy, że w tym wypadku będziemy mieli tylko “sukces” wykonujemy then.

Obsługa – Promise odrzucony

Promise.reject(new Error('to jest błąd!'))
    .then(result => { 
        console.log(result); // nie będzie wywołane
    })
    .catch(error => { 
        console.log(error.message); 
});

W tym wypadku do stworzenia obiektu używamy reject, dlatego też nie zostanie wywołana obsługa “sukcesu”, a jedynie obsługa błędu – catch. Jak widzisz, Promise można rozwiązać z bardziej zaawansowanym obiektem, nie tylko wartością prostą. Zawsze obiekt przekazany do resolve/reject będzie pierwszym argumentem metody obsługującej daną sytuację.

Obsługa – Promise rozstrzygnięty – finally

W javascript `Promise` posiada także finally wywoływane zawsze po rozstrzygnięciu. Można o tym przeczytać w dokumentacji Promise.prototype.finally. Z jakiegoś powodu jednak Promise w TypeScript takiej metody już nie ma.

Wykorzystanie Promise w (bardziej) realnej sytuacji

Powyższe przykłady były trochę bez sensu – ponieważ z założenia zawsze wiedzieliśmy, co się stanie. Teraz spróbuję pokazać, jak zrobić funkcję, która zwraca Promise i wywołując ją nie jesteśmy w stanie stwierdzić, co się stanie.

import * as Promise from 'bluebird'; // wyjaśnienie poniżej 

function getRandomNumber (): Promise<string> { 
    console.log('getRandomNumber');
    return new Promise((resolve, reject) => {
        console.log('getRandomNumber -> Promise');
        const isEven: boolean = parseInt((Math.random()*100).toFixed(0)) % 2 === 0;
        if (isEven) { 
            setTimeout(() => resolve('Parzysta liczba!'), 1000);
        } else {
            setTimeout(() => reject('Nieparzysta liczba :('), 1000);
        } 
     });
}

console.log('Przed wywołaniem getRandomNumber'); 
getRandomNumber()
    .then(result => { console.log(result) }) 
    .catch(error => { console.log(error) }); 
console.log('Po wywołaniu getRandomNumber');

Zwróć proszę uwagę, że zamiast metod Promise.resolve/Promise.reject użyłem tu konstruktora obiektu Promise, który przyjmuje funkcję z dwoma parametrami: resolve i reject. Użycie wciąż całkiem łatwe 🙂

Powyższy kod zwróci:

Przed wywołaniem getRandomNumber 
getRandomNumber 
getRandomNumber -> Promise 
Po wywołaniem getRandomNumber 
Parzysta liczba! # choć tu może być "Nieparzysta liczba :("

Uwaga – błąd kompilacji

Gdy zapisałem powyższy kod w pliku async.ts i spróbowałem uruchomić za pomocą komend:

tsc async.ts
node async.js

Otrzymałem błąd kompilacji:

async.ts(5,16): error TS2693: 
'Promise' only refers to a type, but is being used as a value here.

Najwidoczniej nie jestem jedynym człowiekiem, który trafił na ten problem. Znalazłem stronę na stackoverflow z tym zagadnieniem. Rozwiązanie, które u mnie działało to:

Zainstalować moduł:

npm i --save bluebird

dodać import:

import * as Promise from 'bluebird';

Na koniec

To oczywiście jeszcze nie koniec wszystkich zagadnień związanych z asynchronicznością w JavaScript/TypeScript. W najbliższym czasie spróbuję trochę więcej na ten temat napisać :).

 

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *