All posts

Stop Being Afraid Of Promises And Be Awesome Instead

Every day we struggle with the asynchronous world of JavaScript. That was a real war two years ago. All these animations, requests to the servers with “clever” API forcing us to make requests for each entity in their database, as well as usual timeouts made us write lots of callbacks. Let’s see a short example of how we can get all shops with ingredients for an apple pie.

Here we can see four callbacks: two for getting results and two for handling errors. It doesn’t look pretty, does it? Ok, but what if we have a more complicated example with cache and lots of modules. All the requested data may be used by some modules or widgets in your application, and each of them will make a call to the server to get the data. Of course, we should keep in mind progress elements that have to be shown in the right place until we get responses. The only way out is to synchronize all requests. And here is the hero that will save the day – a promise object.

promises

Use of Promises

There are plenty of sources that provide information about it. For example, the most basic specification known under the name CommonJS Promises/A and its a bit extended version Promises/A+.
To make a long story short, the promise object stores the current state of request. It means that every piece of code that has reference to this object may know about this request and its state, whether it is fulfilled, failed or still in progress. Also, we may attach some handlers that will be called when the state of the request changes.
There are many types of promises: A, B, C, D, KISS. All of them use the same concept of storing the current state of object, but have a different set of methods and names for the promise object. However, all of them try to make our life easier by hiding all transport, error handling and collecting results logic, as well as giving us a way to deal with business logic.
Let’s see a small and useful example. We have an object that is called “connection” to request something from the server. It has a method “request” that takes request parameters and returns a promise object. We have to wait for a response and show a progress element for better user experience. In this example, I won’t use any library to create promises, it’s just a pseudocode to explain the point.

Nice. We’ve separated two different modules. A module to show the progress and a module to analyze data from the server. For now we don’t need to bother about calling the stop method to remove progress element from DOM. We can forget about it and it will be self-removed. Besides, the problem with two nested callbacks was solved. We may read all this code as an English text, if we give right names to the functions: “Request a pie, then get ingredients, then request shops, then show all opened shops”.

That was the easiest part of the way of using promises. Let’s add cache and two independent widgets that will request almost the same data from the server. First of all, we should create a cache that will always return a promise object. If there is already a value in cache, this promise object will be already fulfilled and the method “then” will be called immediately. If there is no such value, it will be requested on the server. A key for each value is a stringified JSON for request. Here is a code snippet:

It’s the simplest implementation of cache without timeout to invalidate values and set methods. It has just one method “get”. When we try to get some value from cache, it will return a promise object. The promise object may be either fulfilled or wait for response. As you can see, if we call the method “get” two times, we’ll get the same promise object. It helps us prevent two similar requests to the server. Also, we can keep all our widgets or modules independent. Let’s see how we can use the cache in our app.

We have two widgets: one to show the ingredients for the chosen pie and the other to show shops where we can buy all of them. These are two independent widgets that don’t know about each other. We should also use our progress module. We need to change it a little bit and add another argument to the “showUntilRequestsAreFinished” method that will store a reference to the element above, which progress should be shown. Let’s see the code:

As you can see, all these modules make the same request to get an apple pie recipe by its id. But our “clever” cache prevents us from repeating this action and in the second time (for recipeWidget that is in another event loop because of timeout) it will return the promise that is created by the first call (for shopsWidget). These two widgets wait for the same response from server. When a response comes the shopsWidget gets all data first of all, and only after it makes another call for the shops,  the recipeWidget will get the same data.

A Real Promise Implementation

Well, the pseudocode looks great but it has one big disadvantage: it doesn’t work. There are two ways to solve this problem: create your own SuperMegaCoolestPromiseWithBlackjack or use the existing libraries.
The first way is great, if you don’t want to depend on some third-party libraries. You may choose whatever implementation you like and add some more methods. But you should be careful, there are lots of tricky stuff around it. That’s why we have here the second way.
As I said, there are lots of libraries to create promises:

  1. Q
  2. when.js
  3. RSVP.js
  4. Deferred (as a part of jQuery or Dojo)
  5. Many others like WinJS, and so on.

All of them support the model that is described in specification (but be careful with jQuery 1.9 and less. Its implementation of promises is a bit different. The method “then” doesn’t return a promise and you can’t chain all your requests).
Before diving into the implementation of promises, I should explain one more thing, that is a deferred object. As you understood, a promise object represents the current state of some async operation. You can read its state, but you can’t change it. To change the state of promise you should use the deferred object connected with it. All deferred objects have at least two methods (to reject and fulfill the connected promise) and one property (connection with this deferred object promise). These methods are often named reject and resolve (fulfill). The deferred object can also have a “notify” method to show progress.
All libraries, of course, have a constructor for deferred objects and usual promises that have “then” and some other fancy methods. Let’s look closer to their APIs.

1. Q.js

It’s a small library for creating promises (~2.5 KB minified and gzipped). It can be used with nodejs and in a browser. Also Q can exchange promises with jQuery, Dojo, When.js, WinJS, and more. It creates a global object “Q” that is a function. A promise can be created like this: Q(some value). Q.defer() is used to create deferred object that can be rejected, resolved (or fulfilled, the same as resolved), notified (to show progress). The deferred object is used to control the state of promise. For example, we’d like to make some asynchronous action, return a promise object and resolve (or reject) it when the action will be finished. Let’s see another implementation of the recipeWidget:

There is also another way to create promises and change their status. You should call the method Promise and pass a callback. This callback is named a resolver and has two arguments: a function to fulfill and reject the created promise:

Ok. The deferred object has quite a simple interface. The most interesting part of this library is the promise object. It includes a lot of useful methods and these are the most important:

  1. then – the same as in the specification;
  2. inspect – returns an object with the current state of the promise and its value;
  3. all – returns a promise that will be fulfilled when all promises that are passed in array as an argument are fulfilled;
  4. spread – is like “then”, but “spreads” the array into a variadic fulfillment handler. It means that if we apply it to the promise that we get from that “all” method, the amount of arguments will be the same as the length of an array that is passed to the “all”:
  5. methods to check the state of promise: isReject, isPending, isFulfilled;
  6. timeout – returns a promise that will be rejected if it isn’t fulfilled during the certain period of time;
  7. lots of sugar methods like thenResolve, thenReject, that can be replaced with the “then” method.

Let’s see a real example of using this library. In first example, we will request three images one by one (for preventing fast loading I’ll add some delay with Fiddler). Each request will create a new deferred object and return a promise. When image is loaded I’ll resolve corresponding promise and made a next request. Here is the code:

promises 1

It’s not the best solution for loading images one by one because it’s quite long. In the second example, I’ll download all images at the same time and show when all of them are loaded. I’ll use method “all”:

promises 2

2.When.js

This library is almost the same as the previous one. To create a deferred object we should use when.defer(). The deferred and the promise object have the same methods. However, there are some fancy sugar methods that can make our life a bit simpler:

1) All array method: map, reduce, some. They are similar to the array methods;

2) any – this method takes an array of promises and returns a promise that will be fulfilled when one of the promises from array is fulfilled;

3. RSVP.js

It’s the smallest library for creating promise objects. It has the same method to create deferred objects. It has a classic Promise/A+ implementation.
Besides, it is extended with events. You may subscribe to and unsubscribe from some events like “created”, “chained”, “fulfilled”, “rejected”. It also includes a build-in EventTarget module that can turn some of your objects into the event target and all event handlers may be bound with this object:

Other ways of promise implementations are almost the same. Note that some of them may not support all features of classic promises (like promises in JQuery 1.9 and less).

Future of Promises

In the end, I’d like to say a few words about native browser implementation of promises. Yes, it’s our future and it’s not so far. You may create native promises right now in Chrome 32 and Firefox 27. The interface is simple. There is a global constructor Promise. You have to call this constructor and pass resolver as a first parameter. The created promise has only two methods “then” and “catch” for now:

Thanks for your attention. You are welcome to leave your comments below and share this article.

The following two tabs change content below.
Vladimir Dashukevich

Vladimir Dashukevich

XB Software developer with extensive experience in different areas of web programming. JavaScript and Graph theory lover. He enjoys applying new technologies and tricky algorithms in Web.
Vladimir Dashukevich

Latest posts by Vladimir Dashukevich (see all)

All posts

Comments(0)


Tags

Recent Posts

Leave a comment

+ twenty one = twenty six

Notify me when new comments are added