最细的实现剖析:jQuery 2.0.3源码分析Deferred

Deferred的概念请看第一篇

http://www.cnblogs.com/aaronjs/p/3348569.html

**构建Deferred对象时候的流程图**

**源码解析**

因为callback被剥离出去后,整个deferred就显得非常的精简

jQuery.extend({
Deferred:function(){}

when:function()
)}
对于extend的继承这个东东,在之前就提及过jquery如何处理内部jquery与init相互引用this的问题

对于JQ的整体架构一定要弄懂 http://www.cnblogs.com/aaronjs/p/3278578.html

所以当jQuery.extend只有一个参数的时候,其实就是对jQuery静态方法的一个扩展

我们在具体看看2个静态方法内部都干了些什么:

Deferred整体结构:

源码精简了部分代码

Deferred:function(func){
vartuples=[
//action,addlistener,listenerlist,finalstate
["resolve","done",jQuery.Callbacks("oncememory"),"resolved"],
["reject","fail",jQuery.Callbacks("oncememory"),"rejected"],
["notify","progress",jQuery.Callbacks("memory")]
],
state="pending",
promise={
state:function(){},
always:function(){},
then:function(/fnDone,fnFail,fnProgress/){},
//Getapromiseforthisdeferred
//Ifobjisprovided,thepromiseaspectisaddedtotheobject
promise:function(obj){}
},
deferred={};
jQuery.each(tuples,function(i,tuple){
deferred[tuple[0]+"With"]=list.fireWith;
});
promise.promise(deferred);
//Alldone!
returndeferred;
},
显而易见Deferred是个工厂类,返回的是内部构建的deferred对象
tuples 创建三个$.Callbacks对象,分别表示成功,失败,处理中三种状态
创建了一个promise对象,具有state、always、then、primise方法
扩展primise对象生成最终的Deferred对象,返回该对象
这里其实就是3个处理,但是有个优化代码的地方,就是把共性的代码给抽象出来,通过动态生成了

具体源码分析:

Deferred自身则围绕这三个对象进行更高层次的抽象

触发回调函数列表执行(函数名)
添加回调函数(函数名)
回调函数列表(jQuery.Callbacks对象)
deferred最终状态(第三组数据除外)
vartuples=[
//action,addlistener,listenerlist,finalstate
["resolve","done",jQuery.Callbacks("oncememory"),"resolved"],
["reject","fail",jQuery.Callbacks("oncememory"),"rejected"],
["notify","progress",jQuery.Callbacks("memory")]
],
这里抽象出2组阵营:

1组:回调方法/事件订阅

done,fail,progress
2组:通知方法/事件发布

resolve,reject,notify,resolveWith,rejectWith,notifyWith
tuples 元素集 其实是把相同有共同特性的代码的给合并成一种结构,然后通过一次处理

jQuery.each(tuples,function(i,tuple){
varlist=tuple[2],
stateString=tuple[3];
promise[tuple[1]]=list.add;
if(stateString){
list.add(function(){
state=stateString;
//[reject_list|resolve_list].disable;progress_list.lock
},tuples[i^1][2].disable,tuples[2][2].lock);
}
deferred[tuple[0]]=function(){
deferredtuple[0]+"With";
returnthis;
};
deferred[tuple[0]+"With"]=list.fireWith;
});
对于tuples的3条数据集是分2部分处理的

第一部分将回调函数存入

promise[tuple[1]]=list.add;
其实就是给promise赋予3个回调函数

promise.done=$.Callbacks("oncememory").add
promise.fail=$.Callbacks("oncememory").add
promise.progressl=$.Callbacks("memory").add
如果存在deferred最终状态

默认会预先向doneList,failList中的list添加三个回调函数

if(stateString){
list.add(function(){
//state=[resolved|rejected]
state=stateString;
//[reject_list|resolve_list].disable;progress_list.lock
},tuples[i^1][2].disable,tuples[2][2].lock);
}


这里有个小技巧

i ^ 1 按位异或运算符

所以实际上第二个传参数是1、0索引对调了,所以取值是failList.disable与doneList.disable


通过stateString有值这个条件,预先向doneList,failList中的list添加三个回调函数

分别是:

doneList:[changeState,failList.disable,processList.lock]
failList:[changeState,doneList.disable,processList.lock]
changeState 改变状态的匿名函数,deferred的状态,分为三种:pending(初始状态), resolved(解决状态), rejected(拒绝状态)
不论deferred对象最终是resolve(还是reject),在首先改变对象状态之后,都会disable另一个函数列表failList(或者doneList)
然后lock processList保持其状态,最后执行剩下的之前done(或者fail)进来的回调函数
所以第一步最终都是围绕这add方法

done/fail/是list.add也就是callbacks.add,将回调函数存入回调对象中
第二部分很简单,给deferred对象扩充6个方法

resolve/reject/notify 是 callbacks.fireWith,执行回调函数
resolveWith/rejectWith/notifyWith 是 callbacks.fireWith 队列方法引用
最后合并promise到deferred

promise.promise(deferred);
jQuery.extend(obj,promise)
所以最终通过工厂方法Deferred构建的异步对象带的所有的方法了

return 内部的deferred对象了

由此可见我们在

vardefer=$.Deferred();//构建异步对象
的时候,内部的对象就有了4个属性方法了

deferred: Object
always: function () {
done: function () {
fail: function () {
notify: function () {
notifyWith: function ( context, args ) {
pipe: function ( / fnDone, fnFail, fnProgress / ) {
progress: function () {
promise: function ( obj ) {
reject: function () {
rejectWith: function ( context, args ) {
resolve: function () {
resolveWith: function ( context, args ) {
state: function () {
then: function ( / fnDone, fnFail, fnProgress / ) {
promise: Object
always: function () {
done: function () {
fail: function () {
pipe: function ( / fnDone, fnFail, fnProgress / ) {
progress: function () {
promise: function ( obj ) {
state: function () {
then: function ( / fnDone, fnFail, fnProgress / ) {
state: "pending"
tuples: Array[3]
构造图

以上只是在初始化构建的时候,我们往下看看动态执行时候的处理

*执行期***

一个最简单的demo为例子

vard=$.Deferred();
setTimeout(function(){
d.resolve(22)
},0);
d.then(function(val){
console.log(val);
})
当延迟对象被 resolved 时,任何通过 deferred.then或deferred.done 添加的 doneCallbacks,都会被调用。回调函数的执行顺序和它们被添加的顺序是一样的。传递给 deferred.resolve() 的 args 参数,会传给每个回调函数。当延迟对象进入 resolved 状态后,再添加的任何 doneCallbacks,当它们被添加时,就会被立刻执行,并带上传入给 .resolve()的参数

换句话说,我们调用d.resolve(22) 就等于是调用。

匿名函数并传入参数值 22

function(val){
console.log(val);//22
}
当前实际的使用中会有各种复杂的组合情况,但是整的外部调用流程就是这样的

* resolve的实现 ***

我们回顾下,其实Deferred对象,内部的实现还是Callbacks对象,只是在外面再封装了一层API,供接口调用

d.resolve(22)
实际上调用的就是通过这个代码生成的

deferred[tuple[0]]=function(){
deferredtuple[0]+"With";
returnthis;
};
deferred[tuple[0]+"With"]=list.fireWith;
deferred.resolveWith()
最终执行的就是 list.fireWith

callbacks.fireWith()
所以最终又回到回调对象callbacks中的私有方法fire()了

Callbacks会通过

callbacks.add()

把回调函数给注册到内部的list = []上,我们回来过看看

deferred.then()

d.then(function(val){
console.log(val);
})
* then的实现 ***

then:function(/fnDone,fnFail,fnProgress/){
varfns=arguments;
returnjQuery.Deferred(function(newDefer){
jQuery.each(tuples,function(i,tuple){
varaction=tuple[0],
fn=jQuery.isFunction(fns[i])&&fns[i];
//deferred[done|fail|progress]forforwardingactionstonewDefer
deferred[tuple[1]](function(){
//省略............
});
});
fns=null;
}).promise();
},
递归jQuery.Deferred
传递了func
链式调用了promise()
因为在异步对象的方法都是嵌套找作用域属性方法的

这里我额外的提及一下作用域

vard=$.Deferred();
这个异步对象d是作用域是如何呢?

第一层:无可争议,浏览器环境下最外层是 window

第二层:jquery本身是一个闭包

第三层: Deferred工厂方法产生的作用域

如果用d.then()方法呢?

很明显then方法又是嵌套在内部的函数,所以执行的时候都默认会包含以上三层作用域+自己本身函数产生的作用域了

我们用个简单图描绘下

演示文稿1

根据规则,在最内部的函数能够访问上层作用域的所有的变量

我们先从使用的层面去考虑下结构设计:

demo 1

vardefer=$.Deferred();
varfiltered=defer.then(function(value){
returnvalue*2;
});
defer.resolve(5);
filtered.done(function(value){
console.log(value)//10
});
demo 2

vardefer=$.Deferred();
defer.then(function(value){
returnvalue2;
}).then(function(value){
returnvalue
2;
}).done(function(value){
alert(value)//20
});
defer.resolve(5);
其实这里就是涉及到defer.then().then().done() 链式调用了

API是这么定义的:

deferred.then(doneFilter[,failFilter][,progressFilter])
从jQuery 1.8开始, 方法返回一个新的promise(承诺),通过一个函数,可以过滤deferred(延迟)的状态和值。替换现在过时的deferred.pipe()方法。 doneFilter 和 failFilter函数过滤原deferred(延迟)的解决/拒绝的状态和值。 progressFilter 函数过滤器的任何调用到原有的deferred(延迟)的notify 和 notifyWith的方法。 这些过滤器函数可以返回一个新的值传递给的 promise(承诺)的.done() 或 .fail() 回调,或他们可以返回另一个观察的对象(递延,承诺等)传递给它的解决/拒绝的状态和值promise(承诺)的回调。 如果过滤函数是空,或没有指定,promise(承诺)将得到与原来值相同解决(resolved)或拒绝(rejected)。

我们抓住几点:

返回的是新的promise对象
内部有一个滤器函数
从demo 1中我们就能看到

经过x.then()方法处理的代码中返回的this(filtered ),不是原来的$.Deferred()所有产生的那个异步对象(defer )了

所以,每经过一个then那么内部处理的this都要被重新设置,那么为什么要这样处理呢?

源码

then:function(/fnDone,fnFail,fnProgress/){
varfns=arguments;
//分别为deferred的三个callbacklist添加回调函数,根据fn的是否是函数,分为两种情况
returnjQuery.Deferred(function(newDefer){
jQuery.each(tuples,function(i,tuple){
varaction=tuple[0],
fn=jQuery.isFunction(fns[i])&&fns[i];
//deferred[done|fail|progress]forforwardingactionstonewDefer
deferred[tuple[1]](function(){
varreturned=fn&&fn.apply(this,arguments);
if(returned&&jQuery.isFunction(returned.promise)){
returned.promise()
.done(newDefer.resolve)
.fail(newDefer.reject)
.progress(newDefer.notify);
}else{
newDefer[action+"With"](this===promise?newDefer.promise():this,fn?[returned]:arguments);
}
});
});
fns=null;
}).promise();
},
在Deferred传递实参的时候,支持一个flag,jQuery.Deferred(func)

传递一个回调函数

//Callgivenfuncifany
if(func){
func.call(deferred,deferred);
}
所以newDefer可以看作是

newDefer=$.Deferred();
那么func回调的处理的就是过滤函数了

deferred[tuple[1]](function(){
varreturned=fn&&fn.apply(this,arguments);
if(returned&&jQuery.isFunction(returned.promise)){
returned.promise()
.done(newDefer.resolve)
.fail(newDefer.reject)
.progress(newDefer.notify);
}else{
newDefer[action+"With"](this===promise?newDefer.promise():this,fn?[returned]:arguments);
}
});
这里其实也有编译函数的概念,讲未来要执行的代码,预先通过闭包函数也保存起来,使其访问各自的作用域

第一步

分解tuples元素集

jQuery.each(tuples,function(i,tuple){
//过滤函数第一步处理
})
第二步

分别为deferred[ done | fail | progress ]执行对应的add方法,增加过滤函数给done | fail | progress 方法

deferred[tuple[1]](
传入过滤函数
)//过滤函数执行的时候在分解
代码即

deferred[done]=list.add=callback.add
第三步

返回return jQuery.Deferred().promise()

此时构建了一个新的Deferred对象,但是返回的的是经过promise()方法处理后的,返回的是一个受限的promise对象

所以整个then方法就处理了2个事情

构建一个新的deferred对象,返回受限的promise对象
给父deferred对象的[ done | fail | progress ]方法都增加一个过滤函数的方法
我们知道defer.then方法返回的是一个新的jQuery.Deferred().promise()对象

那么我们把defer.then返回的称之为子对象,那么如何与父对象var defer = $.Deferred() 关联的起来的

我看看源码

deferredtuple[1]
deferred其实就是根级父对象的引用,所以就嵌套再深,其实都是调用了父对象deferred[ done | fail | progress 执行add罢了

从图中就能很明显的看到 2个不同的deferred对象中 done fail progress分别都保存了不同的处理回调了

deferred.resolve( args )

当延迟对象被 resolved 时,任何通过 deferred.then或deferred.done 添加的 doneCallbacks,都会被调用
回调函数的执行顺序和它们被添加的顺序是一样的
传递给 deferred.resolve() 的 args 参数,会传给每个回调函数
当延迟对象进入 resolved 状态后,再添加的任何 doneCallbacks,当它们被添加时,就会被立刻执行,并带上传入给.resolve()的参数
流程如图

流程解析:

1 执行fire()方法,递归执行list所有包含的处理方法

2 执行了默认的 changeState, disable, lock 方法、

3 执行过滤函数

根据 var returned = fn.apply( this, arguments )的返回值(称作returnReferred)是否是deferred对象

返回值是deferred对象,那么在returnReferred对象的三个回调函数列表中添加newDeferred的resolve(reject,notify)方法,也就是说newDeferrred的执行依赖returnDeferred的状态

不是函数的情况(如值为undefined或者null等),直接链接到newDeferred的resolve(reject,notify) 方法,也就是说 newDeferrred的执行依赖外层的调用者deferred的状态或者说是执行动作(resolve还是reject或者是notify) 此时deferred.then()相当于将自己的callbacklist和newDeferred的callbacklist连接起来

下面就是嵌套deferred对象的划分了

image

源码还是要靠自己去折腾的,思想的提高比较难的,我们可以借鉴设计的思路,代码书写方式都是有益无害的。

流程的分析已经比较透彻了,下一章在讲解when的实现。

原文链接:http://www.cnblogs.com/aaronjs/p/3356505.html

猜你喜欢

转载自blog.51cto.com/5367378/2131552
今日推荐