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){
- returnvalue2;
- }).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] http://xn--bbrz78bn1gfk8a//
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.bubuko.com/infodetail-2654265.html