Deferred的实现主要是依靠jq的callbaks方法的,他是对callbacks的封装,先来看看callbacks的一段小代码
var cb = $.Callbacks();
cb.add(function(a){
console.log(a)
});
cb.fire('hello world')// 输出a值,hello world;
cb.add(function(a){
console.log(a)
})
可以看出cb在调用fire方法时就会执行回调函数,那么他是怎么实现的呢,其实是这样的,callbacks里面有一个数组list用来存储回调函数,而Callbacks通过闭包来返回一个包装了add,fire这一些方法的对象,并且由于闭包的关系数组list不会被处理,那么add方法就是向list里推入回调函数的,最后fire时只要顺序执行下list里的函数就好了,有兴趣的可以去看一下jq的源码,总之,有4点需要得出
- Point
1 ,这个函数队列里的每一个函数都是靠对象冒充实现的,
2 ,callback.fire() 和 callback.fireWidth()都是可以实现便厉队列的
3 ,callback.fire()的this的来自上下文(表达有些问题,在这是为了区分和fireWith的区别)
4 ,callback.firWith的第一个参数是可以自己定义的,不一定是来自于上下文
看看源码区别下
fireWith: function( context, args ) {
if ( !locked ) {
args = args || [];
args = [ context, args.slice ? args.slice() : args ];
queue.push( args );
if ( !firing ) {
fire();
}
}
return this;
},
// Call all the callbacks with the given arguments
fire: function() {
self.fireWith( this, arguments );
return this;
},
,,,,然后看一下jq callbacks的几个用法
var cb = $.Callbacks('memory');
cb.add(function(a){
console.log(a,'第一次')
});
cb.fire('hello world');
cb.add(function(a){
console.log(a,'第二次')
});
当使用memory时在fire调用后使用add,回调函数就会直接被调用,而callbacks还有'once','locked'的用法,具体可以去看一下callbacks,不具体说callbacks,,事实上看明白可callbacks就大概可以理解deferred是怎么回事了,假如我是这么写
- Point
function easyDef(){
var Def = $.Callbacks('memory once'),
def = {
done:Def.add,
fire:Def.fire,
fireWith:Def.fireWith
}
return def;
}
function defTime(arg){
var def = easyDef(),that = this;
setTimeout(function(){
//由于内部时使用apply调用的,所以必须用 []包起来
def.fireWith(this,['接收到远方的消息:你好!!!'])
},3000);
return def;
}
defTime('向远方发送消息').done(function(data){
console.log(data,this)
//接收到远方的消息:你好!!! Window → http://127.0.0.1:8020/newChat/test/deferr.html
//可以看到this来自于window
})
可以看出一个简单的deferred就实现了,当然真实的不会这么简单,还要考虑很多,下面看看deferred,,,
Deferred拥有三种状况,也就是进程中,成功时,失败后,jq先用数组存储了需要使用的三种状态
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 5 ];
promise[ tuple[ 1 ] ] = list.add;
if ( stateString ) {
list.add(
function() {
state = stateString;
},
tuples[ 3 - i ][ 2 ].disable,
tuples[ 0 ][ 2 ].lock
);
}
list.add( tuple[ 3 ].fire );
deferred[ tuple[ 0 ] ] = function() {
deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
return this;
};
deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
} );
使用jq的each方法,来重写封装callbacks的方法,最后的得到内部的deferred对象,(在这里需要说一下通过$.Deferred()产生的def对象启示是由两个对象组成的,之后通过promise的promise对象方法将内部deferred对象和promise对象组合成了一个终极deferred对象,为了区分,这个deferred对象就叫做内部deferred对象好了,看不明白看下面的图)可以看一下dererred对象到底是怎样的,首先看一下所谓的两个对象
//Deferred的promise对象和deferred对象,使用promise对象的promise方法扩展deferred对象方法,其实就是jq的extends方法
//扩展deferred,返回最终的deferred对象 promise.promise( deferred ); promise = {
state: function() {
return state;
},
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
"catch": function( fn ) {
return promise.then( null, fn );
},
// Keep pipe for back-compat
pipe: function( /* fnDone, fnFail, fnProgress */ ) {
/* */
},
then: function( onFulfilled, onRejected, onProgress ) {
/* */
},
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
},
deferred = {};
上图就是返回的deferred最终的对象,拥有许多方法,done,fail,notify方法就是类似callbacks add方法,可以看最之前的例子
接下来看看内部deferred,由于这些方法都类似,所以就拿done来作为例子 ,在之前的
jQuery.each( tuples, function( i, tuple ) {})过后,那么done本身就有了4个回调函数
fn1用于修改状态,表示在这个回调函数执行后的状态
fn2,fn3用于关闭队列和告知函数队列已不在执行
fn4 是添加一个新建callbacks的fire对象,(这点在讲then时很重要,看看之前callbacks的4点,我说过执行函数队列时时对象冒充的,具体下面讲)
- Point
var defo = $.Deferred();
defo.done(function(data){
console.log(data)
//Object { a: 1, b: 2, c: 3 }
})
defo.resolveWith(this,[{a:1,b:2,c:3}])
//当执行了resolveWith方法时,就会执行队列里的函数,
//遵照上面写的4个fn,加上上面done的一个,所以一共是5个fn将被执行,
//由于jQuery.Callbacks( "once memory" )是打开了memory和once的,所以执行过后,函数队列将被清除,下一次done会直接执行,
//并且回调的参数将一直沿用resolveWith所传参数,可以联想到 jq ajax的def实现,
defo.done(function(data){
console.log(data)
//Object { a: 1, b: 2, c: 3 }
})
$.ajax({
type:"get",
url:"1.json",
async:true
}).done(function(data){console.log(data)})
.done(function(data){
///$.ajax()返回的即使def对象 第二次使用done将不在去请求,因为之前已经成功过了
console.log(data)
});
大致的deferred的源码实现就是这样了,其他还有他的一些方法,看看then的方法是如何实现的,我感觉是比较绕,,不记下来就有点乱,这才是我真正想记下来的目的,其他容易记,这个太绕了,首先看看then的用法
- Point
timeDef = function(target) {
var def = $.Deferred()
setTimeout(function() {
def.resolveWith(def, ['向' + target + '发出问候,问候被接受']);
}, 3000)
//def.promise返回的是promise对象,该对象只有done,fail等加入函数队列类方法
//,没有resolve等执行方法,可以避免外界去执行这个def对象(外界执行def.resolve()就会让def失去意义);
return def.promise();
};
var hi = timeDef('/小明').
then(function(data) {
console.log(data)
// 向/小明发出问候,问候被接受
return timeDef('/小刚');
}).
done(function(data) {
//向/小刚发出问候,问候被接受
console.log(data)
})
hi.done(function(data) {
console.log('状态:' + this.state() + ',你已' + data + '')
}).
then(function(data){
console.log('状态:' + this.state() + ',你已' + data + '')
}).
then(function(data){
console.log(data)
//结果为undefined,因为之前没有返回def对象,过程被关闭,
//that = undefined;
//args = [ returned ];
//上面是源码的话,意思就是,this = undefined;data = undefined
})
then就是对def对象的过程式执行,,只要then的回调return def对象时,就会开启准备 执行下一个 def对象 then可以传入三个参数then(fn,fn,fn),
分别是成功,失败,过程中的回调函数,调用then后会 对为之前的def对象(倘若之前也是then并且then的回调返回了def对象,那么就处理这个返回的对象,如果此回调没有返回对象,那么表示def对象过程已经结束了,继续then已经没有用了,参见上面的代码),
下面这是部分的then源码
jQuery.Deferred( function( newDefer ) {
tuples[ 0 ][ 3 ].add(
resolve(
0,
newDefer,
jQuery.isFunction( onProgress ) ?
onProgress :
Identity,
newDefer.notifyWith
)
);
tuples[ 1 ][ 3 ].add(
resolve(
0,
newDefer,
jQuery.isFunction( onFulfilled ) ?
onFulfilled :
Identity
)
);
tuples[ 2 ][ 3 ].add(
resolve(
0,
newDefer,
jQuery.isFunction( onRejected ) ?
onRejected :
Thrower
)
);
} ).promise();
可以看到 它给tuples的一个callbacks添加了队列函数, 我暂时称它为then_callback,下面也是这样,可以参照上面的图,再来看一下吧
所以添加的是fn4,fire方法,所以当xx.then的xx执行好了后,就会执行xx的fn1,fn2,..函数,而fn4被执行后(也就是fire()被执行)所以就会执行then_callbacks,而then方法被执行后就是在已在then_callbacks推入了队列函数,所以将开始执行队列函数,可见下面,以下是源码,部分已被我部分删掉,resolve执行的回调
function resolve( depth, deferred, handler, special ) {
return function() {
var that = this,
args = arguments,
mightThrow = function() {
var returned, then;
if ( depth < maxDepth ) {
return;
}
//对象冒充执行 args就是数据
returned = handler.apply( that, args );
//如果有returned 就是说then的回调函数有return def对象
then = returned &&
( typeof returned === "object" ||
typeof returned === "function" ) &&
returned.then;
//then是否为函数
if ( jQuery.isFunction( then ) ) {
//对progress的处理
if ( special ) {
then.call(
returned,
resolve( maxDepth, deferred, Identity, special ),
resolve( maxDepth, deferred, Thrower, special )
);
} else {
maxDepth++;
//为return的def对象添加了then_callbacks的函数队列
then.call(
returned,
resolve( maxDepth, deferred, Identity, special ),
resolve( maxDepth, deferred, Thrower, special ),
resolve( maxDepth, deferred, Identity,
deferred.notifyWith )
);
}
// Handle all other returned values
} else { //then没有返回return
if ( handler !== Identity ) {
that = undefined;
args = [ returned ];
}
//当return的def对象执行了后,把参数,上下文传给本then,使其可以继续有过程
//a.then(return def) def执行, a.then返回的一个def被执行,并且参数来自def
( special || deferred.resolveWith )( that, args );
}
},
上面大概就是这样,话理的其实不是很清楚,不知道下次来看会不会蒙圈,有问题欢迎指出
好文要顶 关注我 收藏该文
ice_Jf.S关注 - 0
粉丝 - 0 +加关注 0 0