Deferred and Promise.

The Deferred object is a chainable utility object created by calling the jQuery.Deferred() method. It can register multiple callbacks into callback queues, invoke callback queues, and relay the success or failure state of any synchronous or asynchronous function.

Usually, the way we are used to deal with asynchronous code in Javascript is passing callbacks as an argument to the function:


$.ajax({
    url: "/echo/json/",
    data: {json: JSON.stringify({firstName: "Jose", lastName: "Romaniello"})} ,
    type: "POST",
    success: function(person){
        alert(person.firstName + " saved.");
    },
    error: function(){
        alert("error!");
    }
});

This work but it is not an standard interface, it requires some work inside the post method and we can’t register multiple callbacks.

$.Deferred

 

The deferred object has two methods: resolve and reject. And it has three events done, fail, always.

For example:


var deferred = $.Deferred();

deferred.done(function(value) {
   alert(value);
});

deferred.resolve("hello world");

If you call reject method the fail callback will be executed.

Promise()

The deferred object has promise() method. It returns an object almost with the same interface as deferred, but it only has the methods for callbacks and does not have resolve and reject. For example, the $.ajax method in JQuery returns a Promise, so you can do:


var post = $.ajax({
    url: "/api/putObject/",
    data: {json: JSON.stringify({firstName: "Kobe", lastName: "James"})} ,
    type: "POST"
});

post.done(function(p){
    alert(p.firstName +  " has been saved.");
});

post.fail(function(){
    alert("error!");
});

Pipe()

The pipe() methods allows you to “project” a promise. We can change previous example like this:


var post = $.post("/api/putObject/",
        {
            json: JSON.stringify({firstName: "Kobe", lastName: "James"})
        }
    ).pipe(function(p){
        return "Saved " + p.firstName;
    });

post.done(function(obj){ alert(obj); });

Here we are doing a projection of the result which is a “person” object. So, instead of having a Deferred of person, we have now a deferred of “Saved {firstName}”.

Another interesting use case is recursive deferred. Imagine that you started an asynchronous operation in the backend, and you need to do “polling” to see if the task is done and when the task is done do something else.


function getPrintingStatus(){
    var d = $.Deferred();
    $.post(
        "/echo/json/",
        {
            json: JSON.stringify( {status: Math.floor(Math.random()*8+1)} ),
            delay: 2
        }
    ).done(function(s){
        d.resolve(s.status);
    }).fail(d.reject);
    return d.promise();
}

function pollUntilDone(){
    //do something
    return getPrintingStatus()
            .pipe(function(s){
                if(s === 1 || s == 2) {
                    return s;  //if the status is done or cancelled return the status
                }
                //if the status is pending... call this same function
                //and return a deferred...
                return pollUntilDone();
            });
}

$.blockUI({message: "Loading..."});

pollUntilDone()
    .pipe(function(s){ //project the status code to a meaningfull string.
            switch(s){
            case 1:
                return "done";
            case 2:
                return "cancelled";
            }
    })
    .done(function(s){
        $.unblockUI();
        alert("The status is " + s);
    });

Promises and $.when()

$.when accepts an arbitrary number of promises, and it returns a master deferred that:
- will be “resolved” when all the promises are resolved,
- will be rejected if any of the promises is rejected.

$.when( $.ajax( "/page1.php" ), $.ajax( "/page2.php" ) ).done(function( a1, a2 ) {
  // a1 and a2 are arguments resolved for the page1 and page2 ajax requests, respectively.
  // Each argument is an array with the following structure: [ data, statusText, jqXHR ]
  var data = a1[ 0 ] + a2[ 0 ]; // a1[ 0 ] = "Whip", a2[ 0 ] = " It"
  if ( /Whip It/.test( data ) ) {
    alert( "We got what we came for!" );
  }
});

Promises and then()

then() adds handlers to be called when the Deferred object is resolved, rejected, or still in progress. It returns promise, this allows promises to be chained. See examples below.

$.when( $.ajax( "/page1.php" ), $.ajax( "/page2.php" ) )
  .then( myFunc, myFailure );

Chaining:

var request = $.ajax( url, { dataType: "json" } ),
  chained = request.then(function( data ) {
    return $.ajax( url2, { data: { user: data.userId } } );
  });
 
chained.done(function( data ) {
  // data retrieved from url2 as provided by the first request
});

Useful information about promises: http://promisesaplus.com

That’s all for now.

Share this post:Tweet about this on TwitterShare on Facebook0Share on LinkedIn0Share on Google+0Share on Reddit0Email this to someoneDigg this