jQuery Deferred / Promise

 

Today's web development is becoming more and more complex, and there are more asynchronous operations (such as Ajax ). For asynchronous, the usual approach is to use callbacks, which are called to perform subsequent actions when things are completed. But when there are too many layers of callback or waiting for multiple asynchronous events at the same time, the code will be messy, difficult to manage, and error-prone (callback hell).

After an event is executed, the function that continues to be executed is the so-called callback function.

Too many layers of callback? like this...

step1(function (value1) {
  step2(value1, function(value2) {
    step3(value2, function(value3) {
      step4(value3, function(value4) {
        // Do something with value4
      });
    });
  });
});

jQuery provides Deferred to better handle these asynchronous problems. Defer literally means delay, and deferred object is an object that is delayed until a certain point in the future to execute.

Create Deferred object-jQuery.Deferred()

var dfd = $.Deferred();

deferred.done(), deferred.fail(), deferred.always()

When deferred object processing is completed and successful, the callback registered through deferred.done() will be executed; when deferred object processing fails, the callback registered through deferred.fail() will be executed; regardless of success or failure, it will be executed through deferred .always() registered callback.

var dfd = $.Deferred();

dfd.done(function() {
    alert('Succeeded');
}).fail(function() { // Cascade
    alert('Failed');
});

// You can register a new callback with deferred object at any time
dfd.always(function() {
    alert('Regardless of success or failure');
});

deferred.resolve(), deferred.reject()

But how to complete a deferred object and then execute the corresponding callback functions?

The deferred object has three execution states-incomplete, completed, and failed. We can use resolve to change the execution status to success; use reject to change the execution status to failure.

deferred.resolve() ends the execution state of the deferred object (successful), and executes doneCallbacks, alwaysCallbacks.

deferred.reject() ends the execution state (failure) of the deferred object, and executes failCallbacks, alwaysCallbacks.

The state can only be changed once, and cannot be resolved and rejected.
var dfd = $.Deferred();
 
dfd.done(function() {
    alert('You clicked the success button');
});

dfd.fail(function() {
    alert('You clicked the fail button');
});
 
$('button.success').on('click', function() {
  // Notification successful
  dfd.resolve();
});

$('button.fail').on('click', function() {
  // Notification failed
  dfd.reject();
});

The resolve() and reject() methods can also accept a parameter, which is used to pass in the callback function.

var dfd = $.Deferred();
 
dfd.done(function(name) {
    alert('Your name is ' + name);
});
 
$('button').on('click', function() {
  dfd.resolve('Mike');
});

deferred.then(doneCallbacks, failCallbacks)

Sometimes to save trouble, we can use .then() to write .done() and .fail() together.

dfd.then(
  function() {
    alert('succeeded');
  }, function() {
    alert('failed!');
  }
);

deferred.state()

Use deferred.state() to get the current execution state, there are three return values:

  • "pending": Incomplete
  • "resolved": Completed
  • "rejected": failed

jQuery.when(deferreds)

.when() allows you to specify a callback for multiple Deferred events, and then execute this callback after all asynchronous events are over.

var d1 = $.Deferred();
var d2 = $.Deferred();
var d3 = $.Deferred();
 
$.when(d1, d2, d3).done(function (v1, v2, v3) {
  console.log(v1); // v1 is undefined
  console.log(v2); // v2 is "abc"
  console.log(v3); // v3 is an array [1, 2, 3, 4, 5]
});
 
d1.resolve();
d2.resolve('abc');
d3.resolve(1, 2, 3, 4, 5);

deferred.promise()

Promise and Deferred are very similar, except that the Promise object lacks methods to change the state -.resolve(), .reject(). What are the benefits? If you have a function that needs to return a deferred object, but you don't want other programs to change the state randomly, you can change it to return a promise!

function asyncEvent() {
  var dfd = jQuery.Deferred();
 
  // Resolve state after a few seconds of random count
  setTimeout(function() {
    dfd.resolve('hurray');
  }, Math.floor(400 + Math.random() * 2000));
 
  // Reject status after a few seconds of random count
  setTimeout(function() {
    dfd.reject('sorry');
  }, Math.floor(400 + Math.random() * 2000));
 
  // Return a promise to avoid being messed up
  return dfd.promise();
}
 
// Attach a done, fail, and progress handler for the asyncEvent
$.when( asyncEvent() ).then(
  function(status) {
    alert(status + ', things are going well');
  },
  function( status ) {
    alert(status + ', you fail this time');
  }
);

Post a Comment

0 Comments