jQuery Deferred对象详细源码分析(-)
本系列文章讲介绍这个Deferred东西到底拿来干什么,从1.5版本加进来,jQuery的很多代码都重写了。直接先上源码分析了,清楚了源码分析,下节将讲具体的应用 以及应用场景。
创建对象 var def=$.Deferred(); 包含 done,resolve,resolveWith,reject,rejectWith,isResolved等等方法
这套系列文章主要是分析jQuery的源码
1 jQuery.extend({
2 // Create a simple deferred (one callbacks list)
3 _Deferred: function () {
4 var // callbacks list
5 callbacks = [],
6 //储存上下文环境和函数参数 stored [ context , args ]
7 fired,
8 //避免函数提前触发
9 firing,
10 //标示这个延迟对象是否被取消了 cancel方法
11 cancelled,
12 // the deferred itself
13 deferred = {
14
15 /*把要依次处理的函数(arguments)[可以是函数列表,也可以是函数数组]加入到队列表(callbacks)中,,返回 deferred 对象
16 */
17 done: function () {
18 if (!cancelled) {
19 //arguments函数参数对象,可以看出jQuery喜欢把一个函数所有的参数定义到头部
20 var args = arguments,
21 i,
22 length,
23 elem,
24 type,
25 _fired;
26 //如果这个延迟对象已经触发,用 _fired保存[context , args ]
27 if (fired) {
28 _fired = fired;
29 fired = 0; //修改fired=0,以便以后继续调用done()函数
30 }
31 for (i = 0, length = args.length; i < length; i++) {
32 elem = args[i];
33 //这里调用了静态的$.type方法
34 type = jQuery.type(elem);
35 //do([f1,f2]),继续回头调用这个方法
36 if (type === "array") {
37 deferred.done.apply(deferred, elem);
38 } else if (type === "function") {
39 //callbacks 函数集合
40 callbacks.push(elem);
41 }
42 }
43 //如果这个延迟对象已触发,立即调用resolveWith方法
44 if (_fired) {
45 deferred.resolveWith(_fired[0], _fired[1]);
46 }
47 }
48 return this;
49 },
50
51 /*在给定的上下文(context默认为deferred=this对象)中执行队列,清除队列 resolve 执行队列,返回 deferred 对象
52 */
53 resolveWith: function (context, args) {
54 //没有被取消&&没有被已经触发&&避免提前触发
55 if (!cancelled && !fired && !firing) {
56 // make sure args are available (#8421)
57 args = args || [];
58 //阻止callbacks提前触发
59 firing = 1;
60 try {
61 //循环执行回调函数队列,不错的写法
62 while (callbacks[0]) {
63 callbacks.shift().apply(context, args);
64 }
65 }
66 finally {
67 //修改deferred里的变量状态,firing=0 阻止回调函数触发,fired store[context,args]
68 //其实这里的fired firing相当于deferred对象的私有变量,通过改变他的值判断函数队列的执行。
69 fired = [context, args];
70 firing = 0;
71 }
72 }
73 return this;
74 },
75
76 /* resolve with this as context and given arguments,这里的this指代deferred对象
77 */
78 resolve: function () {
79 //当resolve后,以后done()进来的函数都会立即执行,这个在$(function(){});运用的非常好!这里$(function(){})=
$(document).ready(function(){})原因可以在init实例化的函数中看到
if (jQuery.isFunction(selector)) {return rootjQuery.ready(selector)};
80 deferred.resolveWith(this, arguments);
81 return this;
82 },
83
84 /* Has this deferred been resolved?为什么要用!!了,这样才能返回true或者false,比如!!1 !!0 通过deferred对象私有变量判断是否已被
resolved
85 */
86 isResolved: function () {
87 return !!(firing || fired);
88 },
89
90 // Cancel cancelled=1可以阻止callbacks触发,并清空callbacks.这个函数其实我们自己用不到jQuery后面扩展我们使用的Deferred对象时候会取消掉
91 cancel: function () {
92 cancelled = 1;
93 callbacks = [];
94 return this;
95 }
96 };
97
98 return deferred;
99 },
100
101 /*这个$.Deferred()才是给我们使用的,然后根据私有的_Deferred对象扩展出fail,reject等等,这个其实跟done,resolve是同理的,所有作者这里进行了代码公用,只是取了个不同的名字
102 */
103 Deferred: function (func) {
104 //建立两个私有的延迟对象,扩展deferred,用failDeferred去代替fail ,rejectWith ,reject
105 var deferred = jQuery._Deferred(),
106 failDeferred = jQuery._Deferred(),
107 promise;
108 //增加错误的deferred methods,and promise
109 jQuery.extend(deferred, {
110 //两个参数,一个成功回调函数队列,一个失败回调函数队列
111 then: function (doneCallbacks, failCallbacks) {
112 deferred.done(doneCallbacks).fail(failCallbacks);
113 return this;
114 },
115 //不管失败与否都调用
116 always: function () {
117 // done的上下文设置为deferred,fail的上下文设置为this
118 // done和fail的上下文不一致吗?一致!在这里this等于deferred
119 //这行代码同 deferred.done(arguments).fail(arguments);这里感觉有点怪,为了让这个函数队列arguments执行,不得不在done和fail队列同时添加一种回调函数队列然后 return this;但是后面后删除done队列或者fail队列,看那个函数被执行了 答案在这deferred.done(failDeferred.cancel).fail(deferred.cancel);
120 return deferred.done.apply(deferred, arguments).fail.apply(this, arguments);
121 },
122 //这几个方法跟上面done,resolveWith,resolve,isResolve的同理
123 fail: failDeferred.done,
124 rejectWith: failDeferred.resolveWith,
125 reject: failDeferred.resolve,
126 isRejected: failDeferred.isResolved,
127 pipe: function (fnDone, fnFail) {
128 //暂时没搞懂这个拿来干什么,直到怎么理解的可以留下言谢谢
129 return jQuery.Deferred(function (newDefer) {
130 //遍历一个对象
131 jQuery.each({
132 done: [fnDone, "resolve"],
133 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);
141 if (returned && jQuery.isFunction(returned.promise)) {
142 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 /* 返回一个包含 done fail isResolved isRejected promise then always pipe的deferred对象,不让外部修改状态,只能读状态
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 //又一个经典的循环方法,这样更快,这样i--是跟0比较,而不用跟promiseMethods.length比较,学到吧
165 while (i--) {
166 obj[promiseMethods[i]] = deferred[promiseMethods[i]];
167 }
168 return obj;
169 }
170 });
171 // 成功队列执行完成后,会执行失败带列的取消方法
172 // 失败队列执行完成后,会执行成功队列的取消方法
173 // 确保只有一个函数队列会被执行,即要么执行成功队列,要么执行失败队列;
174 // 即状态只能是或成功、或失败,无交叉调用
175 deferred.done(failDeferred.cancel).fail(deferred.cancel);
176 // Unexpose cancel
177 delete deferred.cancel;
178 //如果有函数传进来则立即执行 传入deferred对象,调用回调函数,如def=$.Deferred(funciton(defer){ defer.resolve;.. })
179 if (func) {
180 func.call(deferred, deferred);
181 }
182 //返回deferred对象
183 return deferred;
184 },
185
186 /* Deferred helper
187 异步队列工具函数
188 firstParam:一个或多个Deferred对象或JavaScript普通对象
189 */
190 when: function (firstParam) {
191 //如果参数的长度等于1,并且存在promise函数,表面是一个deferred对象,它这样判断传入的参数是个deferred对象。如果都不满足则新建一个$.Deferred()对象
192 var args = 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 // 构造成功(resolve)回调函数
200 function resolveFunc(i) {
201 return function (value) {
202 // 如果传入的参数大于一个,则将传入的参数转换为真正的数组 sliceDeferred=[].slice
203 args[i] = arguments.length > 1 ? sliceDeferred.call(arguments, 0) : value;
204 //直到count为0的时候
205 if (!(--count)) {
206 // Strange bug in FF4:
207 // Values changed onto the arguments object sometimes end up as undefined values
208 // outside the $.when method. Cloning the object into a fresh array solves the issue
209 //resolve deferred 响应这个deferred对象,上面这句话好像是解决一个奇怪的bug
210 deferred.resolveWith(deferred, sliceDeferred.call(args, 0));
211 }
212 };
213 }
214 if (length > 1) {
215 for (; i < length; i++) {
216 //存在agrs[i]并且是args[i]是deferred对象,那这样的话作者怎么不直接jQuery.isFunction(args[i].promise),感觉多判断了,作者也蒙了吧
217 if (args[i] && jQuery.isFunction(args[i].promise)) {
218 //执行一次resolveFunc(i)count就减少一个
219 args[i].promise().then(resolveFunc(i), deferred.reject);
220 } else {
221 // 计数器,表示发现不是Deferred对象,而是普通JavaScript象 ,反正最后只要count==0才能resovle deferred
222 --count;
223 }
224 }
225 if (!count) {
226 deferred.resolveWith(deferred, args);
227 }
228 } else if (deferred !== firstParam) { //如果只传了一个参数,而这个参数又不是deferred对象,则立即resolve
229 deferred.resolveWith(deferred, length ? [firstParam] : []);
230 }
231 return deferred.promise(); //返回deferred只读视图
232 }
233 });