jquery deferred objects using the detailed
jquery is developed very quickly, almost every half-year, a small version every two months. Each version will introduce some new features. What I want to introduce today is a new feature--deferred object that was introduced from the jquery version 1.5.0. This feature is important and the future will be the core approach to jquery, which radically changes how Ajax is used in jquery. To implement it, all of the Ajax code for jquery is rewritten.
However, it is more abstract, difficult for beginners to master, online tutorials are not many. So, I put my study notes sorted out, I hope to be useful to everyone.
This article is not a beginner's tutorial and is intended for developers who already have the experience of using jquery.
One, what is the deferred object?
In the process of developing a Web site, we often experience some very lengthy javascript operations. There are both asynchronous operations (such as AJAX reading Server data), and synchronous operations (such as traversing a large array), none of which can immediately result.
A common workaround is to specify a callback function (callback) for them. That is, in advance, which functions should be called once they are finished.
However, in terms of callback functions, jquery has a very weak function. To change this, the jquery development team designed the deferred object.
Simply put, the deferred object is the callback function solution for jquery. In English, defer means "delay", so the meaning of the deferred object is "delay" to a certain point in the future.
It solves the problem of how to handle time-consuming operations, provides better control over those operations, and a unified programming interface. Its main function can be summed up to four points. Here we go through the example code, step-by-step learning.
Second, the Ajax operation of the chain-type wording
The Ajax operation of jquery, the traditional writing is this:
$.ajax ({
URL: "Test.html",
Success:function () {
Alert ("Haha, success!") ");
},
Error:function () {
Alert ("Wrong!") ");
}
});
(Run code example 1)
In the above code, $.AJAX () accepts 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 are using jquery that is less than 1.5.0, the XHR object is returned and you cannot perform a chain operation; If the version is higher than 1.5.0, the deferred object is returned and can be chained.
Now, the new wording is this:
$.ajax ("test.html")
. Done (function () {alert ("Haha, success!") "); })
. Fail (function () {alert ("Error!) "); });
(Run code example 2)
As you can see, the done () equivalent to the Success method, fail () is equivalent to the error method. With the chain style, the readability of the code is greatly improved.
Three, specifying multiple callback functions for the same operation
One of the great benefits of deferred objects is that it allows you to add multiple callback functions freely.
Or take the above code as an example, if the Ajax operation succeeded, in addition to the original callback function, I would like to run a callback function, how to do?
It's simple, just add it to the back of the line.
$.ajax ("test.html")
. Done (function () {alert ("Haha, success!") ");} )
. Fail (function () {alert ("Error!) "); } )
. Done (function () {alert ("Second callback function!) ");} );
(Run code example 3)
The callback function can add as many as you want, and they are executed in the order of addition.
Specifying callback functions for multiple operations
Another great benefit of the deferred object is that it allows you to specify a callback function for multiple events, which is not possible with traditional writing.
Take a look at the code below, which uses a new method $.when ():
$.when ($.ajax ("test1.html"), $.ajax ("test2.html"))
. Done (function () {alert ("Haha, success!") "); })
. Fail (function () {alert ("Error!) "); });
(Run code example 4)
This code means that two operations $.ajax ("test1.html") and $.ajax ("test2.html") are executed first, and if successful, the done () specified callback function is executed, and if one fails or fails, the callback function specified by the fail () is performed.
Common Operation callback function interface (upper)
The great advantage of the deferred object is that it extends this set of callback function interfaces from Ajax operations to all operations. That is, any operation----either an AJAX or a local operation, whether asynchronous or synchronous----can use various methods of the deferred object to specify the callback function.
Let's look at a concrete example. Suppose there is a time-consuming operation wait:
var wait = function () {
var tasks = function () {
Alert ("Execution complete!") ");
};
SetTimeout (tasks,5000);
};
We specify a callback function for it, what should we do?
Naturally, you will think that you can use $.when ():
$.when (Wait ())
. Done (function () {alert ("Haha, success!") "); })
. Fail (function () {alert ("Error!) "); });
However, there is a problem. The $.when () parameter can only be a deferred object, so you must overwrite wait:
var DTD = $. Deferred (); Create a new Deferred object
var wait = function (DTD) {
var tasks = function () {
Alert ("Execution complete!") ");
Dtd.resolve (); Change the execution state of a deferred object
};
SetTimeout (tasks,5000);
return Dtd.promise ();
};
Here are two places to pay attention to.
First, the last line cannot return the DTD directly, and must return Dtd.promise (). The reason is that jquery states that any one of the deferred objects has three execution states----unfinished, completed, and failed. If the default execution status of Dtd,$.when () is returned directly, the following done () method is immediately triggered, which loses the function of the callback function. The purpose of dtd.promise () is to ensure that the current execution state----that is, "incomplete"----unchanged, so that the callback function is not triggered until the operation is complete.
Second, when the operation completes, the deferred object's execution state must be manually changed, otherwise the callback function cannot be triggered. The effect of Dtd.resolve () is to change the execution state of the DTD from incomplete to completed, triggering the Done () method.
Finally, remember that after you modify the wait, you must pass in the DTD parameter directly when you call.
$.when (Wait (DTD))
. Done (function () {alert ("Haha, success!") "); })
. Fail (function () {alert ("Error!) "); });
(Run code example 5)
Normal operation of the callback function interface (in)
In addition to using $.when () to add callback functions for normal operations, you can also use the constructor $ of the deferred object. Deferred ().
At this point, the wait function remains the same, and we pass it directly into $. Deferred ():
$. Deferred (Wait)
. Done (function () {alert ("Haha, success!") "); })
. Fail (function () {alert ("Error!) "); });
(Run code example 6)
jquery rules,. Deferred () can accept a function as an argument, which will be in the. Deferred () executes before the result is returned. And, $. The Deferred object generated by Deferred () will be the default argument for this function.
Normal operation of the callback function interface (next)
In addition to the above two methods, we can also deploy the deferred interface directly on the wait object.
var DTD = $. Deferred (); Generating deferred objects
var wait = function (DTD) {
var tasks = function () {
Alert ("Execution complete!") ");
Dtd.resolve (); Change the execution state of a deferred object
};
SetTimeout (tasks,5000);
};
Dtd.promise (wait);
Wait.done (function () {alert ("Haha, success!") "); })
. Fail (function () {alert ("Error!) "); });
Wait (DTD);
(Run code example 7)
The key here is the dtd.promise (wait) line, which is the role of deploying the deferred interface on the wait object. It is because of this line that you can invoke the done () and fail () directly above the wait.
Viii. Summary: Methods of deferred objects
There are a number of ways to deferred objects, and here's a summary:
(1) $. Deferred () generates a Deferred object.
(2) Deferred.done () specifies the callback function when the operation succeeds
(3) Deferred.fail () specifies the callback function when the operation fails
(4) when deferred.promise () has no parameters, the effect is to keep the running state of the deferred object unchanged; When you accept the parameter, you deploy the deferred interface on the Parameter object.
(5) Deferred.resolve () manually change the running state of the deferred object to "completed", triggering the Done () method immediately.
(6) $.when () specifies a callback function for multiple operations.
In addition to these methods, the deferred object has three important methods, which are not covered in the tutorials above.
(7) Deferred.then ()
Sometimes for the sake of convenience, you can put the done () and fail () together to write, this is the then () method.
$.when ($.ajax ("/main.php"))
. Then (Successfunc, Failurefunc);
If then () has two parameters, the first parameter is the callback function of the Done () method, and the second parameter is the callback method of the Fail () method. If then () has only one parameter, then it is equivalent to done ().
(8) Deferred.reject ()
This method, in contrast to Deferred.resolve (), triggers the Fail () method immediately after the call changes the running state of the deferred object to "failed".
(9) Deferred.always ()
This method is also used to specify the callback function, and its function is to always execute regardless of whether the call is Deferred.resolve () or Deferred.reject ().
$.ajax ("test.html")
. Always (function () {alert ("Executed!"). ");} );
JQuery Deferred Object detail source Analysis
This series of articles describes what the deferred is all about, from the 1.5 version, and many of jquery's code is rewritten. Directly above the source analysis, clear the source analysis, the next section will talk about the specific application and application scenarios.
Create object var def=$. Deferred (); Include Done,resolve,resolvewith,reject,rejectwith,isresolved and so on
This series of articles is mainly to analyze the jquery source
1 Jquery.extend ({
2//Create A simple deferred (one callbacks list)
3 _deferred:function () {
4 var//Callbacks List
5 callbacks = [],
6//Storage context and function parameters stored [contexts, args]
7 Fired,
8//Avoid early triggering of functions
9 Firing,
10//Indicates whether this delay object has been canceled cancel method
One cancelled,
A//the deferred itself
Deferred = {
14
15/* Add the function (arguments) to be processed sequentially (either a list of functions or an array of functions) to the Queue table (callbacks), and return the deferred object
16 */
Done:function () {
if (!cancelled) {
//arguments function Parameter object, you can see that jquery likes to define all parameters of a function to the head
var args = arguments,
I,
Length,
Elem,
Type,
_fired;
26//If this delay object has been triggered, save with _fired [context, args]
if (fired) {
_fired = fired;
Fired = 0; Modify Fired=0 so that you can continue to invoke the done () function later
30}
for (i = 0, length = args.length i < length; i++) {
Elem = Args[i];
33//Here the static $.type method is called
The type = Jquery.type (elem);
//do ([F1,f2]), go back and call this method
(Type = = "Array") {
Panax Notoginseng deferred.done.apply (deferred, elem);
/else if (type = = "function") {
//callbacks function set
Callbacks.push (Elem);
41}
42}
43//If this delay object has been triggered, call the Resolvewith method immediately
if (_fired) {
Deferred.resolvewith (_fired[0], _fired[1]);
46}
47}
The return of this;
49},
50
51/* Executes the queue in the given context (the contextual defaults to the Deferred=this object), clears the queue resolve execution queue, and returns the deferred object
52 */
Resolvewith:function (context, args) {
54//has not been canceled && has not been triggered && avoid early triggering
if (!cancelled &&!fired &&!firing) {
/Make sure args are available (#8421)
args = args | | [];
58//Prevent callbacks from triggering early
firing = 1;
A try {
61//Loop execution callback function queue, good writing
while (Callbacks[0]) {
Callbacks.shift (). Apply (context, args);
64}
65}
and finally {
67//Modify variable state in deferred, firing=0 block callback function trigger, fired Store[context,args]
68//In fact, here the fired firing equivalent to the deferred object's private variable, by changing his value to judge the execution of the function queue.
Fired = [context, args];
firing = 0;
71}
72}
The return of this;
74},
75
* * Resolve with the as context and given arguments, here This refers to the deferred object
77 */
Resolve:function () {
79//When the resolve, the later done () function will be executed immediately, this in the $ (function () {}); use it very well! Here $ (function () {}) =
$ (document). Ready (function () {}) reason can be seen in the function of Init instantiation
if (Jquery.isfunction (selector)) {return Rootjquery.ready (selector)};
Deferred.resolvewith (this, arguments);
I return to this;
82},
83
* * Has this deferred been resolved why use!! , so that you can return TRUE or FALSE, for example!! 1!! 0 by deferred object private variable to determine whether it has been
Resolved
85 */
Isresolved:function () {
Return!! (Firing | | fired);
88},
89
A//Cancel cancelled=1 can prevent callbacks from triggering and empty the callbacks. This function is actually not going to be able to use our own back up the deferred object we're using.
Cancel:function () {
Cancelled = 1;
callbacks = [];
The return of this;
95}
96};
97
The return deferred;
99},
100
101/* This $. Deferred () is for us to use, and then according to the private _deferred to expand the fail,reject and so on, this is actually the same with done,resolve, all the authors here for the code common, just take a different name
102 */
Deferred:function (func) {
104//Establish two private delay object, extend deferred, replace fail, Rejectwith, reject with faildeferred
The deferred var = jquery._deferred (),
The faildeferred = jquery._deferred (),
Promise;
108//Add the wrong deferred Methods,and promise
109 Jquery.extend (Deferred, {
110//Two parameters, a successful callback function queue, a failed callback function queue
Then:function (Donecallbacks, failcallbacks) {
112 Deferred.done (donecallbacks). Fail (failcallbacks);
113 return to this;
114},
115//No matter whether the failure is called
116 Always:function () {
The 117//done context is set to the Deferred,fail context set to this
are 118//done and fail contexts inconsistent? Consistent! Here this is equal to deferred
119//This line of code is the same as deferred.done (arguments). Fail (arguments); it feels a little weird here, in order for this function queue arguments to execute, it has to add a A callback function queue and then return to this; But after you delete the done queue or the fail queue, see that function is executed in this deferred.done (Faildeferred.cancel). Fail (deferred.cancel );
Deferred.done.apply (deferred, arguments). fail.apply (this, arguments);
121},
122//These methods are similar to the above Done,resolvewith,resolve,isresolve
123 Fail:failDeferred.done,
124 RejectWith:failDeferred.resolveWith,
Reject:failDeferred.resolve,
126 isRejected:failDeferred.isResolved,
127 pipe:function (Fndone, Fnfail) {
128//For the time being, I don't know what to do with this, until I understand what I can say thank you.
129 return jquery.deferred (function (newdefer) {
130//Traverse an Object
131 Jquery.each ({
132 Done: [Fndone, "resolve"],
Fail: [Fnfail, "reject"]
134}, function (handler, data) {
135 var fn = data[0],
136 action = data[1],
137 returned;
138 if (Jquery.isfunction (FN)) {
139 Deferred[handler] (function () {
140 returned = Fn.apply (this, arguments);
if (returned && jquery.isfunction (returned.promise)) {
Returned.promise (). Then (Newdefer.resolve, Newdefer.reject);
143} else {
144 Newdefer[action + "with"] (this = = deferred? Newdefer:this, [returned]);
145}
146});
147} else {
148 Deferred[handler] (newdefer[action]);
149}
150});
151}). Promise ();
152},
153
154/* Returns a then object containing the done fail isresolved isrejected promise always pipe, which does not allow external modification state, only read state
155 */
156 promise:function (obj) {
157 if (obj = = null) {
158 if (Promise) {
159 return promise;
160}
161 Promise = obj = {};
162}
163 var i = promisemethods.length;
164//Another classic cycle method, so faster, so i--is compared with 0, and not with promisemethods.length comparison, learn it
165 while (i--) {
166 Obj[promisemethods[i]] = deferred[promisemethods[i]];
167}
The return obj;
169}
170});
171///After successful queue execution completes, the failed with column cancellation method is executed
172//Failed queue execution completes, execution of successful queue cancellation method
173//Ensure that only one function queue is executed, that is, either execute the success queue or execute the failed queue;
174//That State can only be or succeeded, or failed, no cross call
175 Deferred.done (Faildeferred.cancel). Fail (deferred.cancel);
176//Unexpose Cancel
177 Delete deferred.cancel;
178//If a function is passed in, execute the incoming deferred object immediately, call the callback function, such as def=$. Deferred (Funciton (defer) {defer.resolve;.})
179 if (func) {
180 Func.call (deferred, deferred);
181}
182//Return deferred object
183 return deferred;
184},
185
186/* Deferred Helper
187 Asynchronous Queue Tool functions
188 Firstparam: One or more deferred objects or JavaScript generic objects
189 */
190 When:function (Firstparam) {
191//If the length of the argument is equal to 1 and there is a promise function, the surface is a deferred object, which determines that the incoming parameter is a deferred object. If none is satisfied, create a new $. Deferred () object
The args var = arguments,
193 i = 0,
194 length = Args.length,
195 count = length,
196 deferred = length <= 1 && firstparam && jquery.isfunction (firstparam.promise)?
197 Firstparam:
198 jquery.deferred ();
199//Construction Success (resolve) callback function
The function Resolvefunc (i) {
201 return function (value) {
202//If the incoming argument is greater than one, the incoming argument is converted to a true array Slicedeferred=[].slice
203 Args[i] = arguments.length > 1? Slicedeferred.call (arguments, 0): value;
204//Until count is 0.
205 if (!) ( --count)) {
206//Strange bug in FF4:
207//Values changed onto the arguments object sometimes end up as undefined Values
$.when//Outside the method. Cloning the object into a fresh array solves the issue
209//resolve Deferred response to this deferred object, the above sentence seems to solve a strange bug
210 Deferred.resolvewith (deferred, Slicedeferred.call (args, 0));
211}
212};
213}
214 if (length > 1) {
215 for (; i < length; i++) {
216//exists agrs[i] and is args[i] is the deferred object, then how does the author not direct jquery.isfunction (args[i].promise), feel more judge, the author also monk
217 if (Args[i] && jquery.isfunction (args[i].promise)) {
218//Perform one resolvefunc (i) count reduces a
219 args[i].promise (). Then (Resolvefunc (i), deferred.reject);
The same} else {
221//Counter, indicates that the discovery is not deferred object, but ordinary javascript like, anyway, finally, as long as count==0 can resovle deferred
222--count;
223}
224}
if (!count) {
Deferred.resolvewith (deferred, args);
227}
228} else if (deferred!== firstparam) {//If only one argument is passed, and this parameter is not a deferred object, immediately resolve
229 Deferred.resolvewith (deferred, length?) [Firstparam]: []);
230}
231 return deferred.promise (); Return deferred read-only view
232}
233});