How to use deferred objects in jquery tutorial

  • 2020-03-30 01:27:12
  • OfStack

What is a deferred object?

In the process of developing a website, we often encounter some time-consuming javascript operations. There are both asynchronous operations (such as ajax reading server data) and synchronous operations (such as traversing a large array) that do not yield immediate results.

It is common practice to specify callback functions for them. That is, specify in advance which functions should be called once they are finished running.

However, in terms of callback functions, jQuery is very weak. To change this, the jQuery development team designed deferred objects.

Simply put, deferred objects are jQuery's callback solution. In English, defer means "delay," so deferred object means "defer" until some future point.

It solves the problem of how to handle time-consuming operations, provides better control over those operations, and provides a unified programming interface. Its main functions can be boiled down to four points. Let's take a step by step look at the sample code.

Second, the chain of ajax operations

First, let's review how jQuery's ajax operations are traditionally written:


$.ajax({
url: "test.html",
success: function(){
alert(" Haha, it worked! ");
},
error:function(){
alert(" Error! ");
}
});

In the above code, $.ajax() takes an object parameter that contains two methods: the success method specifies the callback function after the operation succeeds, and the error method specifies the callback function after the operation fails.

After the ajax() operation is completed, if you use jQuery which is less than 1.5.0 and return XHR object, you cannot do the chain operation. If higher than version 1.5.0, a deferred object is returned and chain operations can be performed.

Now, the new way of writing it is this:


$.ajax("test.html")
.done(function(){ alert(" Haha, it worked! "); })
.fail(function(){ alert(" Error! "); });

As you can see, done() corresponds to the success method, and fail() to the error method. After adopting the chain method, the readability of the code is greatly improved.

Specifies multiple callback functions for the same operation

One of the great things about deferred objects is that they allow you to add multiple callbacks.

Again, what if I want to run another callback function in addition to the original callback function after a successful ajax operation?

It's easy, just add it to the end.


$.ajax("test.html")
.done(function(){ alert(" Haha, it worked! ");} )
.fail(function(){ alert(" Error! "); } )
.done(function(){ alert(" Second callback function! ");} );


The callback function can add as many as you like, and they are executed in the order they are added.

Specify callback functions for multiple operations

Another great benefit of deferred objects is that they allow you to specify a single callback function for multiple events, which is not traditionally possible.

Look at the following code, which USES a new method $.when() :


$.when($.ajax("test1.html"), $.ajax("test2.html"))
.done(function(){ alert(" Haha, it worked! "); })
.fail(function(){ alert(" Error! "); });


What this code means is that you first perform two operations, $.ajax("test1.html") and $.ajax("test2.html"), and if both succeed, run the callback function specified by done(). If one or both fails, the callback function specified by fail() is executed.

5. Callback function interface for normal operation (top)

The great thing about deferred objects is that they extend this set of callback interfaces from ajax operations to all operations. That is, any operation -- whether ajax or local, asynchronous or synchronous -- can specify a callback function using various methods of the deferred object.

Let's look at a specific example. Suppose you have a time-consuming operation wait:


var wait = function(){
var tasks = function(){
alert(" Execution complete! ");
};
setTimeout(tasks,5000);
};


So what do we do when we specify a callback function for it?

Naturally, as you might expect, you can use $.when() :


$.when(wait())
.done(function(){ alert(" Haha, it worked! "); })
.fail(function(){ alert(" Error! "); });

However, if written this way, the done() method executes immediately, not as a callback function. The reason is that $.when() can only take a deferred object, so wait() must be overwritten:


var dtd = $.Deferred(); //Create a new deferred object
var wait = function(dtd){
var tasks = function(){
alert(" Execution complete! ");
dtd.resolve(); //Changes the deferred object's execution state
};
setTimeout(tasks,5000);
return dtd;
};

Now that the wait() function returns a deferred object, you can add a chain operation.


$.when(wait(dtd))
.done(function(){ alert(" Haha, it worked! "); })
.fail(function(){ alert(" Error! "); });

When the wait() function runs, the callback specified by the done() method runs automatically.

6. The deferred.resolve() method and the deferred.reject() method

If you look closely, you'll see that there's one thing I didn't cover in the wait() function above. So what does dtd.resolve() do?

To make this clear, introduce a new concept called "execution state". JQuery states that deferred objects have three execution states -- incomplete, completed, and failed. If the execution state is resolved, the deferred object immediately invokes the callback function specified by the done() method. If the execution state is "failed," call the callback function specified by the fail() method. If the execution state is "incomplete," wait or call the callback function specified by the progress() method (added in version 1.7 of the jQuery1.7).

The deferred object will automatically change its execution state based on the result of the ajax operation in the previous part. However, in the wait() function, this execution state must be specified manually by the programmer. DTD. Resolve () means that the execution state of the DTD object is changed from "not done" to "done," thereby triggering the done() method.

Similarly, there is a deferred.reject() method that triggers the fail() method by changing the execution state of the DTD object from "incomplete" to "failed."


var dtd = $.Deferred(); //Create a new Deferred object
var wait = function(dtd){
var tasks = function(){
alert(" Execution complete! ");
dtd.reject(); //Changes the Deferred object's execution state
};
setTimeout(tasks,5000);
return dtd;
};
$.when(wait(dtd))
.done(function(){ alert(" Haha, it worked! "); })
.fail(function(){ alert(" Error! "); });

7. Promise () method

There's still a problem with the way I wrote it up here. That is, a DTD is a global object, so its execution state can be changed externally.

Look at the following code:


var dtd = $.Deferred(); //Create a new Deferred object
var wait = function(dtd){
var tasks = function(){
alert(" Execution complete! ");
dtd.resolve(); //Changes the Deferred object's execution state
};
setTimeout(tasks,5000);
return dtd;
};
$.when(wait(dtd))
.done(function(){ alert(" Haha, it worked! "); })
.fail(function(){ alert(" Error! "); });
dtd.resolve();

I added a line of dtd.resolve() at the end of the code, which changed the execution state of the DTD object, causing the done() method to execute immediately, jumping out of "haha, it worked!" ", wait for 5 seconds before clicking "finished!" "Prompt box.

To avoid this, jQuery provides a deferred. Promise () method. What it does is return another deferred object on the old deferred object, which only exposes methods unrelated to changing the state of execution (such as done() and fail()), and shields methods related to changing the state of execution (such as resolve() and reject()) so that the state of execution cannot be changed.

Look at the following code:


var dtd = $.Deferred(); //Create a new Deferred object
var wait = function(dtd){
var tasks = function(){
alert(" Execution complete! ");
dtd.resolve(); //Changes the Deferred object's execution state
};
setTimeout(tasks,5000);
return dtd.promise(); //Return the promise object
};
var d = wait(dtd); //Create a new d object and operate on it instead
$.when(d)
.done(function(){ alert(" Haha, it worked! "); })
.fail(function(){ alert(" Error! "); });
d.resolve(); //At this point, the statement is invalid


In the code above, the wait() function returns a promise object. Then, we bind the callback to this object instead of the old deferred object. The advantage of this is that you can't change the execution state of the object, you can only operate on the old deferred object to change the execution state.

A better way to write it, however, is as allenm points out, by turning the DTD object into an internal object of the wait() function.


var wait = function(dtd){
var dtd = $.Deferred(); //Inside the function, create a new Deferred object
var tasks = function(){
alert(" Execution complete! ");
dtd.resolve(); //Changes the Deferred object's execution state
};
setTimeout(tasks,5000);
return dtd.promise(); //Return the promise object
};
$.when(wait())
.done(function(){ alert(" Haha, it worked! "); })
.fail(function(){ alert(" Error! "); });


8. Callback function interface for normal operation (middle)

Another way to prevent the execution state from being changed externally is to use the deferred object's constructor, $.deferred ().

At this point, the wait function remains unchanged, and we pass it directly to $.deferred () :


$.Deferred(wait)
.done(function(){ alert(" Haha, it worked! "); })
.fail(function(){ alert(" Error! "); });
 

JQuery states that $.deferred () can take a function name as an argument, and the Deferred object generated by $.deferred () will be the default argument to this function.

9. Callback function interface for normal operation (below)

In addition to the above two methods, we can also deploy the deferred interface directly on the wait object.


var dtd = $.Deferred(); //Generate Deferred object
var wait = function(dtd){
var tasks = function(){
alert(" Execution complete! ");
dtd.resolve(); //Changes the Deferred object's execution state
};
setTimeout(tasks,5000);
};
dtd.promise(wait);
wait.done(function(){ alert(" Haha, it worked! "); })
.fail(function(){ alert(" Error! "); });
wait(dtd);
 

The key here is the dtd.promise(wait) line, which is used to deploy the Deferred interface on the wait object. It is this line that makes it possible to call done() and fail() directly on wait later.

Summary: methods for deferred objects

The various methods of deferred objects have been covered earlier, so here is a summary:

(1) $.deferred () generates a Deferred object.

(2) deferred.done() specifies the callback function if the operation succeeds

(3) deferred.fail() specifies the callback function when an operation fails

(4) deferred. Promise () returns a new deferred object with no parameters, and its running state cannot be changed; When a parameter is accepted, it is used to deploy a deferred interface on the parameter object.

(5) deferred.resolve() manually change the deferred object to "done" to immediately trigger the done() method.

(6) deferred.reject() this method is the opposite of deferred.resolve(), which immediately fires the fail() method by changing the deferred object to "failed."

(7) $.when() specifies a callback function for multiple operations.

In addition to these methods, deferred objects have two important methods that are not covered in the tutorial above.

(8) deferred. Then ()

Sometimes, to make things easier, you can write done() and fail() together, which is the then() method.

$. When ($. Ajax (". / the main PHP "))

Then (successFunc failureFunc);

If then() has two arguments, the first argument is the callback function for the done() method, and the second argument is the callback method for the fail() method. If then() has only one argument, it is equivalent to done().

(9) deferred. Always ()

This method is also used to specify the callback function, which always ends up executing whether the call is deferred.resolve() or deferred.reject().


$.ajax( "test.html" )
.always( function() { alert(" Executed! ");} );


Related articles: