chevron-left chevron-right

[JS] Jak usprawnić logikę kodu Javascript za pomocą Promises, czyli obiecanki Javascriptu

Czym jest technologia AJAX, to chyba dzisiaj każda osoba związana z tworzeniem stron internetowych już wie. Jej obecność na stronach internetowych znacząco ułatwia pobieranie danych z zewnętrznych źródeł.

Do tej pory pisząc kod Javascript, który wysyła zapytania AJAXowe bardzo łatwo było znaleźć się w sytuacji, gdy poziom zagnieżdżeń kodu znacząco utrudniał czytanie kodu, a wystąpienie błędu sprawiało problemy z dalszym działaniem aplikacji.

I Promise you - obietnice w JS

Na szczęście, od jakiegoś czasu, mamy dostęp do funkcjonalności JS zwanej Promises. Promises, czyli obietnice, są to obiekty typu Deferred, które pozwalają nam na tworzenie łańcuchów kodu obsługującego dane z zapytań AJAX (asynchroniczne skrypty JS). Dzięki temu możemy tworzyć swoiste kroki w przetwarzaniu danych z zapytań. Dzięki wykorzystaniu Promises operujemy na obietnicy że te dane mogą się pojawić i na tej podstawie definiujemy jakie są operacje do wykonania na tych danych.

Obiekty typu Deferred

Są to obiekty użytkowe, które mogą przyjmować wiele różnych callbacków (wywołań) i kolejkować je, wywoływać je i przekazywać stan wykonania callback do różnych funkcji. Ponadto, obiekty typu Deferred pozwalają na tworzenie łańcuchów kodu, co pozwala na zwiększenie czytelności kodu.

tłumaczenie na podstawie dokumentacji jQuery API

Myślę, że najprościej będzie to pokazać na bazie przykładu poniżej:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
$.ajax({
  url       : 'js/getUser.php',
  dataType  : 'json',
  data      : {
    userId : 1
  },
  success : function (response) {
    $.ajax({
      url           : 'http://twitter.com/status/user_timeline/' + response.user.twitterName + '.json?count=10',
      dataType      : 'jsonp',
      jsonpCallback : function (twitterResponse) {
        console.log(twitterResponse);
      }
    })
  }
});

Powyższy kod jest odpowiedzialny za pobranie informacji o użytkowniku od identyfikatorze: 1, a następnie, na podstawie zwróconych przez zapytanie AJAX danych pobiera wpisy z profilu użytkownika na Twitterze (to jest przykładowy link i na chwilę obecną on nie działa).

Na powyższym przykładzie można zauważyć dość dużą skalę zagnieżdżeń co sprawia, że śledzenie logiki aplikacji może być utrudnione. BY rozwiązać tego typu problem powstała funkcjonalność Promises w języku JS. Niestety, Promises nie są wspierane natywnie we wszystkich przeglądarkach. Na nasze szczęście, jQuery posiada ich obsługę, dzięki czemu możemy przystąpić do budowy nowej, przejrzystej listy kroków jakie kod JS musi zrobić z pobranymi danymi.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
$.when($.ajax({
  url       : 'js/getUser.php',
  dataType  : 'json',
  data      : {
    userId : 1
  },
  success : function (response) { return response; }
})).then(function (response) {
  var jsonpCallback = function (twitterResponse) {
    return twitterResponse;
  };
 
  $.ajax({
    url           : 'http://twitter.com/status/user_timeline/' + response.user.twitterName + '.json?count=10',
    dataType      : 'jsonp',
    jsonpCallback : jsonpCallback
  });
}).then(function (data) {
  console.log('Twitter response', data);
});

Jak można zauważyć, powyższy zapis kodu jest o wiele czytelniejszy. Od razu widać, która część kodu kiedy jest wywoływana. Wykonanie pierwszego wywołania funkcji .then() będzie miało miejsce tylko wtedy, gdy zapytanie AJAX zwróci odpowiedź z serwera, która zawiera dane. Taki zapis pozwala również na o wiele wygodniejsze zarządzanie błędami kodu, które mogłyby wyniknąć z braku odpowiedzi z danymi lub z jakimikolwiek innymi akcjami po stronie kodu JS.
Taki zapis, jak powyżej, sprawi że cała aplikacja nie przestanie działać, a tylko nie będzie działać ta część strony/aplikacji która miała zostać wywołana w kolejnych krokach realizacji obietnicy JS.

Najpopularniejsze metody jQuery Promises

Oprócz metod: when i then, "obietnice" posiadają szereg innych metod, które pozwalają zdefiniować zachowanie kodu w różnych sytuacjach, np. gdy zapytanie AJAX zostało odrzucone czy też gdy chcemy śledzić postęp w procesie przetwarzania zapytania. Poniżej zrobiłem zestawienie najbardziej popularnych metod dla Promises:

.when(asyncFunction)
Wykonuje funkcję/callback która zwraca Promise. Od tej metody zazwyczaj zaczynamy tworzenie obietnic w JS.
.then(successCallback, errorCallback)
Dodaje kolejny krok do wykonania przez kod JS. Przyjmuje 2 parametry: funkcję/callback gdy Promise jesst spełniony i funkcję/callback gdy Promise nie został spełniony.
.always(callback)
Wykonuje funkcję/callback zawsze. Nie ważne czy Promise zakończył się sukcesem lub błędem.
.progress(callback)
Metoda ta dodaje śledzenie postępu spełniania obietnicy JS
.done(successCallback)
Metoda któ®ej zadaniem jest ostateczne zakończenie łańcucha wywołań obietnic JS, gdy Promise został spełniony
.fail(errorCallback)
Metoda któ®ej zadaniem jest ostateczne zakończenie łańcucha wywołań obietnic JS, gdy Promise nie został spełniony

To jest lista metod, które uważam za najprzydatniejsze w codziennej pracy webmastera. Jeśli chcesz poznać więcej metod, to zapraszam na stronę z dokumentacją API jQuery.

Podsumowanie

Mam nadzieję, że po zaznajomieniu się z Promises w Javascript tworzenie asynchronicznego kodu Javascript nie będzie Ci już sprawiało tyle problemów co wcześniej. Wspomnę jeszcze o tym, że obietnice JS mają kilka specyfikacji, a omówiona w tym artykule przeze mnie wersja nie jest zgodna ze specyfikacją Promises/A+.
Co to oznacza? Oznacza to tyle, że metoda .then() nie zwraca kolejnej obietnicy JS, tylko dane zwrócone przez callback (funkcję zwrotną).

Jeśli jednak chcielibyśmy korzystać z pełni możliwości specyfikacji Promises/A+, to możemy skorzystać z następujących bibliotek: