jQuery, Promises & Deferred
Written by Ian Elliot   
Monday, 17 September 2012
Article Index
jQuery, Promises & Deferred
Chaining and Combining Promises

Chaining Promises

 

 

If you want to perform two asynchronous tasks, one after the other, you can by simply chaining then method calls. As in all function chaining to make it work you have to return the correct sort of object from each function.

In many ways this idea is key to understanding and using promises in more sophisticated ways.

To understand what is going on we need to master one subtle point of the way promises work. The then method always returns a promise object. You can think of this as a default object returned to make sure you can always chain calls on the then function. The default promise has as its value  the value returned by the onComplete function of the original promise object.

For example:

var myPromise = $.get("TextFile.txt");
myPromise
  .then(function (value) {
          console.log(value);
          return "Hello";
       })
 .then(function (value) {
          console.log(value);
       })

     

Now if you run this you will see  "Hello" displayed in the console. Notice the way that the first onComplete function returns "Hello" which is the value of the default promise object returned by then. Also notice that is default promise object is immediately resolved and its onComplete function is executed at once.

What if the first onComplete function was another slow operation?

In this case it should return a promise object so that the UI thread could get on with other things. If you arrange for the onComplete function to return this promise object then it replaces the default promise object for chaining purposes and its value is whatever the onComplete task returns.

For example, suppose we need to download two files one after the other, we might use:

var myPromise = $.get("TextFile1.txt");
myPromise
  .then(function (value) {
    console.log(value);
    return $.get("TextFile2.txt");
    })
  .then(function (value) {
    console.log(value);
   })

In this case the first promise object's then function runs the onComplete function when TextFile1 has finished loading. The onComplete function uses another get to download a second file and returns its promise object.

Notice that, even though the onComplete function has returned, the task it started is still running. The second use of  the then method runs an onComplete function when "TextFile2.txt" has downloaded.

Of course, you might well want to download both files at the same time, but if you care about the order of downloading and need the first file to finish downloading before the second even starts, this is the way to do it.

Chaining promises is a fundamental way to order asynchronous tasks.

This is to be preferred to nested promises e.g.:

var myPromise = $.get("TextFile1.txt");
myPromise.then(
 function (value) {
  console.log(value);
  $.get("TextFile2.txt").then(
     function (value) {
        console.log(value);
  })
})

 

This nests a callback within a callback. It is better to return the promise object and write things like:

 object.then(oncomplete1).then(oncomplete2)

and so on.

Combining Promises

If you have a set of asynchronous tasks that occur one after the other then you need to chain together their promise objects.

However, it is more common to have a set of asynchronous tasks that you want to set running, in parallel if possible, and then do something after they have all completed. In this case what you really need to do is join or combine all the promise objects together into one promise object that is resolved only when all of the original promise objects are resolved.

You can combine promises in this way using the when method. If you specify a list of promise objects in the call to when it returns a single "master" promise object that is only resolved when all of the parameter promise objects are resolved.

The onComplete of the master promise object is passed all of the values that the parameter promise objects return.

If any of the parameter promise objects fail and are rejected then the master promise object fails. This means you could have some promise objects that are still working when the master promise object fails.

As a simple example consider downloading two files:

var myPromise1 = $.get("TextFile1.txt");
var myPromise2 = $.get("TextFile2.txt");
myTotalPromise = $.when(myPromise1, myPromise2); myTotalPromise.then(
 function (value1,value2) {
   console.log(value1)
   console.log(value2);
})

 

If you run this example you will discover that the two values returned by myPromise1 and mPromise2 are complete objects with all of the values returned by each promise - this is general behavior for the when method.

Notice also that you may well have to keep references to the promises that have been combined so that you can clean up after any tasks that fail. The general rule is that asynchronous processes are much harder to deal with when one fails than when they all work.

Other Promise Methods

Most of the time you can get the job done using just the then method of the promise object but jQuery does provide some extras.

Instead of using the then method to define what should happen you can use the done, fail or always methods to add functions to the promise object which are carried out when the task is complete, fails or when the task completes or fails. 

For example the previous example could be written:

var myPromise = $.get("TextFile.txt"); myPromise.done(
 function (value) {
  console.log(value);
 });

 

All three functions can accept any number of functions, or an array of functions, to execute when the promise is resolved. All of the promise methods return a promise so you can chain them and write things like:

 var myPromise = $.get("TextFile.txt")
             .done(function (value) {
                      console.log(value); })
             .fail(function(value){
                      console.log("failed");});

It is up to you to decide on the style that you want to use, but it is worth adding that the then method is the more standard approach to promises. 

jQuery also provides another promise method that is better to avoid if you can. The pipe method lets you add functions or "filters" that can modify the return value of the promise. You can specify a filter for the successful completion, error or progress actions. In each case the filter is called with the value of the promise, it can modify the value and return it to be processed further by the onComplete, error or progress functions. This might be useful in some situations, but it is a facility that is very easy to misuse and you can create programs that are very difficult to debug using it.

Finally the state function can be used to discover the state of a promise - pending, resolved or rejected.

The Deferred Object

I have to confess to a small simplification used throughout the article so far.

jQuery tends to use the Deferred object rather than a promise and it is the Deferred object that is mentioned in the documentation much more than the promise object.

The Deferred object is a promise with some additional methods that enable you to change its state from pending to resolved or rejected and to trigger progress updates.

That is, any Deferred object can be treated as if it was a promise object. Also every Deferred object has a promise method which returns a pure promise object based on the Deferred object.

You use a Deferred object when you are implementing your own asynchronous functions. However, your function may use a Deferred object internally but it should return a promise object to stop other functions from modifying its state.

Some jQuery methods return a Deferred object when they really should return a promise - but you can just treat it as a promise object and restrict yourself to using just then, done, fail etc.

You only really need to know about the Deferred object when you implement your own asynchronous routines that return a promise object, more of which in the second part.

 

Asynchonous jQuery

 

 

 Related Articles

 

 

To be informed about new articles on I Programmer, install the I Programmer Toolbar, subscribe to the RSS feed, follow us on, Twitter, Facebook, Google+ or Linkedin,  or sign up for our weekly newsletter.

 

Banner


Just JavaScript - Object Construction

Object creation is fundamental to all object-oriented languages, but in JavaScript is is left to the programmer to work out how best to do it and often the practice that you encounter isn't the best b [ ... ]



Just JavaScript - The Object Expression

As in most programming languages the expression is an important part of JavaScript, but it isn't quite the same. This is where the idea that JavaScript has some weird type conversions arises. But Java [ ... ]


Other Articles

blog comments powered by Disqus

 

 



Last Updated ( Tuesday, 12 February 2013 )
 
 

   
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.