JQuery Deferred object details

  • 2020-03-30 03:51:31
  • OfStack

The deferred object is jQuery's implementation of the Promises interface. It is a generic interface for asynchronous operations and can be viewed as a task waiting to be completed, which is set by the developer through a number of interfaces. In fact, it ACTS as a proxy, wrapping those asynchronous actions into objects with some uniform features, such as Ajax actions, web animations, web workers, and so on.

All of jQuery's Ajax action functions return a deferred object by default.

What is the Promises

Because of the single-threaded nature of JavaScript, if an operation takes a long time, other operations must be queued. To prevent the entire program from becoming unresponsive, the usual solution is to write the operations that follow, in the form of "callbacks." While this can solve the problem, it has some notable disadvantages:

1. Callback function is often written in the form of function parameters, resulting in the function input and output is very confused, the whole program is poor readability;
2. Callbacks are often limited to one, and if there are more than one operation, the callback function needs to be overwritten.
3. The running process of the whole program is disrupted, and the difficulty of debugging and debugging is correspondingly increased.

Promises is proposed to solve these problems, and its main purpose is to replace callback functions as a solution for asynchronous operations. The idea is to have an asynchronous operation return an object against which all other operations are performed. For example, suppose an ajax operation returns a Promise object.


var promise = get('http://www.example.com');

The Promise object then has a then method that you can use to specify the callback function. Once the asynchronous operation is complete, the specified callback function is called.

promise.then(function (content) {
  console.log(content)
})

You can combine the above two pieces of code to make the flow of the program clearer.

get('http://www.example.com').then(function (content) {
  console.log(content)
})

Prior to version 1.7, jQuery used callback functions for Ajax operations.


$.ajax({
    url:"/echo/json/",
    success: function(response)
    {
       console.info(response.name);
    }
});

After version 1.7, the Ajax action returns the Promise object directly, which means that the callback function can be specified using the then method.


$.ajax({
    url: "/echo/json/",
}).then(function (response) {
    console.info(response.name);
});

Methods of deferred objects

$. Deferred () method

To generate a deferred object.


var deferred = $.deferred();

Done () and fail ()

Both methods are used to bind the callback function. Done () specifies the callback function after an asynchronous operation succeeds, and fail() specifies the callback function after a failure.


var deferred = $.Deferred();
deferred.done(function(value) {
   alert(value);
});

They return the old deferred object, so you can use the chain method and link other methods (including done and fail) later.

The resolve () and reject ()

These two methods are used to change the state of the deferred object. Resolve () changes state to asynchronous operation successful, reject() to operation failure.


var deferred = $.Deferred();
deferred.done(function(value) {
   alert(value);
});
deferred.resolve("hello world");

Once resolve() is called, the callbacks specified by the done() and then() methods are executed in turn; Once reject() is called, the callback function specified by the fail() and then() methods is executed in turn.

State method

This method returns the current state of the deferred object.


var deferred = new $.Deferred();
deferred.state();  // "pending"
deferred.resolve();
deferred.state();  // "resolved"

The method returns three values:

1. Pending: indicates that the operation is pending.
2. Resolved: the operation was successful.
3. I rejected the operation.

Notify () and progress ()

Progress () is used to specify a callback function that will execute when the notify() method is called. Its purpose is to provide an interface that allows you to perform certain actions during the execution of asynchronous operations, such as periodically returning progress on a progress bar.


 var userProgress = $.Deferred();
    var $profileFields = $("input");
    var totalFields = $profileFields.length
    userProgress.progress(function (filledFields) {
        var pctComplete = (filledFields/totalFields)*100;
        $("#progress").html(pctComplete.toFixed(0));
    });
    userProgress.done(function () {
        $("#thanks").html("Thanks for completing your profile!").show();
    });
    $("input").on("change", function () {
        var filledFields = $profileFields.filter("[value!='']").length;
        userProgress.notify(filledFields);
        if (filledFields == totalFields) {
            userProgress.resolve();
        }
    });

Then ()

Then () also specifies the callback function, which takes three arguments, or three callbacks. The first argument is the callback function called when resolve, the second argument is the callback function called when reject, and the third argument is the callback function called by the progress() method.


deferred.then( doneFilter [, failFilter ] [, progressFilter ] )

Before jQuery 1.8, then() was just a syntactic sugar for.done().fail(), which was equivalent. After jQuery 1.8, then() returns a new deferred object, and done() returns the old deferred object. If the callback function specified by then() has a return value, that return value is passed as an argument to the subsequent callback function.


var defer = jQuery.Deferred();
defer.done(function(a,b){
            return a * b;
}).done(function( result ) {
            console.log("result = " + result);
}).then(function( a, b ) {
            return a * b;
}).done(function( result ) {
            console.log("result = " + result);
}).then(function( a, b ) {
            return a * b;
}).done(function( result ) {
            console.log("result = " + result);
});
defer.resolve( 2, 3 );

Before jQuery version 1.8, the result of the above code was:


result = 2
result = 2
result = 2

After jQuery version 1.8, the result is

result = 2
result = 6
result = NaN

This deserves special attention.

$.ajax( url1, { dataType: "json" } )
.then(function( data ) {
    return $.ajax( url2, { data: { user: data.userId } } );
}).done(function( data ) {
  //Data obtained from url2
});

The last done method in the above code deals with data from url2, not url1.

With the feature that then() modifies the return value, we can process the value returned by the previous operation before calling other callback functions.


var post = $.post("/echo/json/")
    .then(function(p){
        return p.firstName;
    });
post.done(function(r){ console.log(r); });

The above code first USES the then() method to fetch the required field (firstName) from the returned data, so the rest of the operation can handle only that field.

Sometimes an Ajax operation returns a json string with an error attribute that indicates an error has occurred. At this point, the traditional way to tell if an error has occurred is by done(). The then() method lets the deferred object call the fail() method.


var myDeferred = $.post('/echo/json/', {json:JSON.stringify({'error':true})})
    .then(function (response) {
            if (response.error) {
                return $.Deferred().reject(response);
            }
            return response;
        },function () {
            return $.Deferred().reject({error:true});
        }
    );
myDeferred.done(function (response) {
        $("#status").html("Success!");
    }).fail(function (response) {
        $("#status").html("An error occurred");
    });

Always ()

Always () is also the specified callback function, called either resolve or reject.

Pipe method

The pipe method takes a function as an argument to run the callback function specified by the pipe method before calling the callback function specified by the then, done, fail, and always methods. It is often used for preliminary processing of data returned from the server.

Promise object

For the most part, we don't want the user to change the state of the deferred object from the outside. At this point, you can return a promise object to the deferred object. The latter can be understood as a promise being a read-only version of deferred, or more colloquially as a promise being a commitment to a task to be completed.

You can use the promise object to add a callback function to the original deferred object and query its state, but you can't change its state, which means the promise object won't allow you to call the resolve and reject methods.


function getPromise(){
    return $.Deferred().promise();
}
try{
    getPromise().resolve("a");
} catch(err) {
    console.log(err);
}

The above code will error, showing TypeError {}.

JQuery's ajax () method returns a promise object. In addition, an Animation class operation can also use a promise object.


var promise = $('div.alert').fadeIn().promise();

$. When () method

$.when() takes multiple deferred objects as arguments and calls the resolved state callback when they all succeed, but calls the rejected state callback whenever one of them fails. It is equivalent to merging multiple asynchronous operations into one.


$.when(
    $.ajax( "/main.php" ),
    $.ajax( "/modules.php" ),
    $.ajax( "/lists.php" )
).then(successFunc, failureFunc);

The above code indicates that the callback function specified by the then method is not executed until all three ajax operations are completed.

The callback function has as many arguments as there are operations to perform in the when method, corresponding to the return result of each of the previous operations.


$.when(
    $.ajax( "/main.php" ),
    $.ajax( "/modules.php" ),
    $.ajax( "/lists.php" )
).then(function (resp1, resp2, resp3){
    console.log(resp1);
    console.log(resp2);
    console.log(resp3);
});

The callback function in the above code takes three arguments, resp1, resp2, and resp3, which in turn correspond to the results returned from the previous three ajax operations.

Another use of the when method is that if its arguments do not return a Deferred or Promise object, the callback to the when method runs immediately.


$.when({testing: 123}).done(function (x){
  console.log(x.testing); // "123"
});

The callback function specified in the above code will run immediately after the when method.

Taking advantage of this feature, we can write an asynchronous operation function with a caching effect. That is, the first time the function is called, the asynchronous operation is performed, and the function is called later, which returns the cached result.


function maybeAsync( num ) {
  var dfd = $.Deferred();
  if ( num === 1 ) {
    setTimeout(function() {
      dfd.resolve( num );
    }, 100);
    return dfd.promise();
  }
  return num;
}
$.when(maybeAsync(1)).then(function (resp){
  $('#target').append('<p>' + resp + '</p>');
});
$.when(maybeAsync(0)).then(function (resp){
  $('#target').append( '<p>' + resp + '</p>');
});

The code above says that if the parameter of the maybeAsync function is 1, then the asynchronous operation is performed, otherwise the cached result is immediately returned.

The instance

Wait method

We can write a wait method with a deferred object to indicate how many milliseconds to wait before executing.


$.wait = function(time) {
  return $.Deferred(function(dfd) {
    setTimeout(dfd.resolve, time);
  });
}

Usage:

$.wait(5000).then(function() {
  alert("Hello from the future!");
});

Overwrite the setTimeout method

On top of the wait method above, you can also override the setTimeout method to return a deferred object.


function doSomethingLater(fn, time) {
  var dfd = $.Deferred();
  setTimeout(function() {
    dfd.resolve(fn());
  }, time || 0);
  return dfd.promise();
}
var promise = doSomethingLater(function (){
  console.log( ' Execution has been delayed ' );
}, 100);

Custom operations use the deferred interface

We can take advantage of the deferred interface so that any action can specify a callback function with done() and fail().


Twitter = {
  search:function(query) {
    var dfr = $.Deferred();
    $.ajax({
     url:"http://search.twitter.com/search.json",
     data:{q:query},
     dataType:'jsonp',
     success:dfr.resolve
    });
    return dfr.promise();
  }
}

Usage:


Twitter.search('intridea').then(function(data) {
  alert(data.results[0].text);
});

Another advantage of deferred objects is that you can attach multiple callbacks.

function doSomething(arg) {
  var dfr = $.Deferred();
  setTimeout(function() {
    dfr.reject("Sorry, something went wrong.");
  });
  return dfr;
}
doSomething("uh oh").done(function() {
  alert("Won't happen, we're erroring here!");
}).fail(function(message) {
  alert(message)
});


Related articles: