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:

  • Bimbrownik

    Mam pytanie 😉 Czy asynchroniczny kod przyczynia się żeczywiście do zwiększenia szybkości wczytwania się strony? Czy robiłeś testy?

  • Asynchroniczny kod może się przyczynić do zwiększenia szybkości wczytywania się strony, ale to zależy od tego czego wymagamy od asynchronicznego kodu. Jeśli mamy bardzo dużo różnorakiej treści na stronie, np. bardzo rozbudowany kod HTML, dużo grafiki, dużo kodu reklamowego, to ładowanie treści strony za pomocą zapytań asynchronicznych przyspieszy ładowanie się strony. Lecz tak jak napisałem wcześniej, to wszystko zależy od tego czego wymagamy od naszego kodu i od naszej strony/aplikacji.

  • Asynchroniczny kod może się przyczynić do zwiększenia szybkości
    wczytywania się strony, ale to zależy od tego czego wymagamy od
    asynchronicznego kodu. Jeśli mamy bardzo dużo różnorakiej treści na
    stronie, np. bardzo rozbudowany kod HTML, dużo grafiki, dużo kodu
    reklamowego, to ładowanie treści strony za pomocą zapytań
    asynchronicznych przyspieszy ładowanie się strony. Lecz tak jak
    napisałem wcześniej, to wszystko zależy od tego czego wymagamy od
    naszego kodu i od naszej strony/aplikacji. Wszystko zależy od wymagań.

  • Bimbrownik

    Właśnie wszystko zależy … Dzięki Piotrze za info.
    Jestem pod dużym wrażeniem Twojego bloga! Że też Ci się che 😉

  • Mateusz Gachowski

    Pełna profeska. Fajnie jakbyś dodał jeszcze jakiś przykład z oczekiwaniem na x różnych zapytań i dopiero po ich sukcesie wykonał akcję. Chodzi mi tu szczególnie o metody resolve(), reject(), promise(). Poza tym świetnie opisane, oby tak dalej!