JavaScript Promises, czy rzeczywiście takie przydatne?

Pamiętam, gdy kilka lat nie doceniałem siły i wartości JavaScriptu. Zmieniło się to zupełnie, kiedy poznałem jQuery. W ostatnich latach powstało wiele projektów i bibliotek, które naprawdę wyciskają prawdziwy sok. Wszystko co nowe, wymaga czasu, cierpliwości i zaangażowania, ale kiedy zrozumiesz podstawy, docenisz zaawansowane techniki. Podobnie było ze mną, gdy pierwszy raz zobaczyłem implementację obiektu Deferred w jQuery 1.5.

Obietnice (ang. Promises) stanowią nowy paradygmat programowania w JavaScripcie. Jednak jego zrozumienie wymaga czasu i pewnej uwagi. Generalnie, Obietnica stanowi wynik pewnego zadania, które się powiodło lub nie. Jedyne wymaganie jakie stawia Obietnica to funkcja then, która określa funkcje zwrotne w przypadku sukcesu lub porażki zadania. Bardzo dokładnie ta koncepcja została opisana w artykule CommonJS Promises/A proposal.

Przykładowo, dla lepszego zrozumienia przedstawimy asynchroniczną operacje zapisu dla naszego obiektu Parse.Object, która w ujęciu funkcji zwrotnych przedstawia się następująco:

object.save({ key: value }, {
  success: function(object) {
    // obiekt zapisany.
  },
  error: function(object, error) {
    // nieudane zachowanie obiektu.
  }
});

Analogiczne zachowanie przy rozważeniu paradygmatu obietnic, równie prosto:

object.save({ key: value }).then(
  function(object) {
    // obiekt zapisany.
  },
  function(error) {
    // nieudane zachowanie obiektu.
  });

Bardzo podobnie? Więc o co tyle szumu? Prawdziwa moc obietnic to wielokrotne wywoływanie i wzajemne łączenie. Wywołanie promise.then(func) zwraca nową obietnicę, która nie spełni się się dopóki func nie zakończy się. Istnieje pewna wyjątkowa kwestia, dotycząca sposobu wywołania funkcji przy udziale obietnic. Jeśli funkcja zwrotna związana z then zwraca nową obietnicę, wówczas obietnica zwrócona przez then nie spełni się, dopóki nie spełni się obietnica funkcji zwrotnej. Brzmi skomplikowanie, ale dokładne zachowanie takiego scenariusza przedstawiono w artykule Promises/A+. Ponieważ przytoczony artykuł jest skomplikowany, omówimy konkretny przykład, który lepiej przedstawi całość zagadnienia.

Wyobraź sobie kod odpowiedzialny za logowanie, szukanie obiektu i wreszcie jego aktualizację. W przypadku starego paradygmatu funkcji zwrotnych, skończy się to wielkim zagnieżdżonym kodem:

Parse.User.logIn("user", "pass", {
  success: function(user) {
    query.find({
      success: function(results) {
        results[0].save({ key: value }, {
          success: function(result) {
            // obiekt zapisany.
          }
        });
      }
    });
  }
});

Szybko taki kod staje się przerażający, nawet bez jakiejkolwiek obsługi błędów. Jednak przy podejściu kolejnych wywołań i zastosowaniu obietnic prezentuje się to bardziej czytelnie:

Parse.User.logIn("user", "pass").then(function(user) {
  return query.find();
}).then(function(results) {
  return results[0].save({ key: value });
}).then(function(result) {
  // obiekt zapisany.
});

O wiele lepiej, czyż nie!

Obsługa błędów

Powyższy kod, prezentuje idealnie wady dotychczasowego podejścia, ponieważ po dodaniu obsługi błędów, kod staje się trudny w zrozumieniu:

Parse.User.logIn("user", "pass", {
  success: function(user) {
    query.find({
      success: function(results) {
        results[0].save({ key: value }, {
          success: function(result) {
            // obiekt zapisany.
          },
          error: function(result, error) {
            // wystąpienie błędu.
          }
        });
      },
      error: function(error) {
        // wystąpienie błędu.
      }
    });
  },
  error: function(user, error) {
    // wystąpienie błędu.
  }
});

Obietnice wiedzą, kiedy zostały spełnione lub nie, zatem spokojnie przekazują błędy bez wywoływania funkcji zwrotnych, dopóki obsługa błędów nie zostanie zakończona. Powyższy kod zapiszemy jeszcze prościej:

Parse.User.logIn("user", "pass").then(function(user) {
  return query.find();
}).then(function(results) {
  return results[0].save({ key: value });
}).then(function(result) {
  // obiekt zapisany.
}, function(error) {
  // wystąpienie błędu.
});

Generalnie, programiści używają niepowodzenia obietnic jako asynchronicznego wariantu rzucenia wyjątku. W rzeczywistości, jeśli funkcja zwrotna przekazana do then zgłosi błąd to zwrócona obietnica, także zakończy się z błędem. Propagowanie błędu do następnego bloku obsługi błędów jest równoważne bąbelkowaniu wyjątku do momentu spotkania instrukcji catch.

jQuery, Backbone oraz Parse

Istnieje wiele implementacji obietnic dostępnych dla programistów. Na przykład, wspomniany na początku obiekt Deffered w jQuery, WinJS.Promise Microsoftu, when.js, q, i dojo.Deferred.

Jest jeszcze jeden istotny punkt widzenia całej sprawy, którego powinniśmy być świadomi. Długa i fascynująca dyskusja dla jQuery, które nie do końca implementuje obsługę specyfikację Promises/A w sposób jaki robią to inne popularne biblioteki. Znalazłem tylko jeden przypadek, w którym oba podejścia rozchodzą się. Jeśli obsługa błędu zwraca coś innego niż obietnicę, większość implementacji rozważa obsługę błędu zamiast jego zgłaszania. Jednakże, jQuery nie rozważa obsługi błędu w tym przypadku, a jedynie jego dalsze zgłaszanie. Pomimo różnych implementacji, całość powinna pracować płynnie, dlatego miej oko na takie sytuacje. Potencjalne rozwiązanie to zwracanie obietnic (zamiast surowych wartości) zawsze podczas obsługi błędów, ponieważ są one traktowane tak samo.

doFailingAsync().then(function() {
  // wywołanie doFailingAsync nie powiodło się.
}, function(error) {
  // Próba obsługi błędu.
  return "It's all good.";
}).then(function(result) {
  // Inne (nie jQuery) implementacje osiągną zakładany wynik, czyli równoważne "It's all good.".
}, function(error) {
  // jQuery osiągnie to z błędem, czyli równoważne "It's all good.".
});

W najnowszym wydaniu Backbone 0.9.10, metody asynchroniczne zwracają teraz obiekt jqXHR, któr jest typem obietnicy jQuery. Jednym z celów dla Parse JavaScript SDK jest utrzymanie jak największe zgodności z Backbone. Nie możemy zwrócić jqXHR, bo to nie będzie dobrze działał w Cloud Code. Rozwiązaniem okazało się stworzenie klasy o nazwie Parse.Promise, która jest zgodna z semantyką obiektu Deffered w jQuery. Ostatnia wersja Parse JavaScript SDK posiada zmodernizowane wszystkie asynchroniczne metody, aby zwracały te nowe obiekty. Stare funkcje zwrotne są nadal akceptowane. Ale w oparciu o przykłady wymienione powyżej, sądzimy, że wolisz nową drogę. Zatem daj obietnicom szansę!

Autorem oryginalnego artykułu What’s so great about JavaScript Promises? jest Bryan Klimt. Parse jest platformą aplikacji w chmurze dla iOS, Android, JavaScript, Windows 8, Windows Phone 8 oraz OS X.

Join the Conversation

Brak komentarzy

  1. Under such circumstances then splitting generally is a good plan to offer you
    yet another possiblity to hit a ten on a single of one’s next cards and improve that
    pair of nines. People awaken every day wondering the most up-to-date information and gossip regarding the athletes they follow.
    The versatility and simplicity on this game has flawing waves of
    fascination to the peoples all around the globe.

  2. Your favorite casino will acquire a single of those excellent slots,
    enjoy today so you might be another slots millionaire. The term „Neighbors of Zero” means the numbers that are placed between twenty-two and twenty-five about the Roulette wheel.

    Totes and providers that happen to be actively seeking gambling opportunities also give rise to Ranogajec’s huge income.

  3. Thanks to the latest Playtech developments, there is a variety of traditional games from classic video slots to high skilled blackjack games,
    offered at no download flash mode. Some even have tutorials in items like poker tricks to attract
    a large number with the clientele. However, understanding some from the basic things about
    the action can be helpful in winning.

  4. You should recognize that when it comes to roulette, there is really no actual method that you are able to go about influencing the result from the game.

    Stone, who’s from Bradford, started his career in 1991 in the renowned Rainbow Casino.
    They are licensed within the Real Time Gaming licensure and possess over 100 different games to provide to
    their customers online.

  5. Ԝith havin sⲟ much content and aгticleѕ do you ever гun into
    any problems of pⅼagоriѕm or copyright infringement?
    My site has a lot of unique content I’ve eitheг created myself
    or outѕourced but іt appears а lot of іt is popping it up all over
    the internet without my authorіzation. Do you knoԝ any solutions to help protect
    against content from being stolen? I’d truly appreciate іt.

  6. At this time it looks like BlogEngine is the preferred blogging platform out there right
    now. (from what I’ve read) Is that what you’re using on your blog?

Dodaj komentarz

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