jQuery.Deferred源码分析
观察者模式:
- 组成部分:发布者和观察者
- 发布者主要负责发布内容,观察者主要负责监听发布者的任务。
- 发布者会维护一个订阅者列表
- 从程序角度来看: 订阅者就是一堆的方法,发布者的推送内容的动作就是依次调用订阅者列表中的方法(订阅者),而发布的内容就将以参数的形式提供给订阅者。
在Deferred中使用观察者模式:
源码分析:
// 可以通过调用方法deferred.done/fail/progress/then/always()添加成功回调函数、失败回调函数、消息回调函数到对应的成功、失败、消息回调函数列表中
// 相当于add方法
// 并且可通过调用方法deferred.resolve/resolveWith/reject/rejectWith/notify/notifyWith()分别触发成功、失败、消息回调函数列表
// 相当于fire方法,只是由于添加了once memory / memory,所以执行过程会有差别
// 异步队列
jQuery.extend({
Deferred: function( func ) {
var tuples = [
// action, add listener, listener list, final state
// jQuery.Callbacks("once memory") 成功回调函数列表
// jQuery.Callbacks("once memory") 失败回调函数列表
// jQuery.Callbacks("memory") 消息回调函数列表
/**
* 为什么是once,就是done fail里面的函数,只能执行一次,再次触发resolve, reject
* done fail里面的函数不会再执行,但是可以通过 done fail 来添加函数,
*
* 而只要notify一触发,那么progress里面的函数就会执行
* 把相同有共同特性的代码整合成一种结构,然后通过一次性处理
*
* tuples定义3中状态的,即resolve,reject,notify。这三种状态分别对应了3中订阅接口(done,fail,progress)
* 和3种订阅列表(3个Callbacks),resolve和reject有最终的状态(resolved和rejected)
*/
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
],
/**
* 有三种状态:
* 1. "pending" : Deferred 对象是尚未完成状态 。
* 2. "resolved" : Deferred 对象是在解决状态
* 3. "rejected" : Deferred 对象是在被拒绝的状态
*/
// 初始状态
state = "pending",
// 异步队列的只读副本
promise = {
state: function() {
return state;
},
// 参数可以是一个function对象,也可以是一个function数组
// 返回一个Deferred对象,失败和成功都会调用arguments
// 添加回调函数,当异步队列处于成功或失败状态时被调用
always: function() {
deferred.done( arguments ).fail( arguments );
return this;
},
// 同时添加成功回调函数、失败回调函数、消息回调函数
then: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;
// 这里使用jQuery.Deferred创建了一个新的延迟对象(newDeder),包含有这个延迟对象所有的属性,方法
return jQuery.Deferred(function( newDefer ) {
jQuery.each( tuples, function( i, tuple ) {
// resolve reject notify
var action = tuple[ 0 ],
// console.log(1 && 2); 2
// console.log(null && 2); null
// console.log(undefined && 2); undefined
fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
// deferred[ done | fail | progress ] for forwarding actions to newDefer
// 这个请看下面图示讲解哦
deferred[ tuple[1] ](function() {
var returned = 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();
},
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
// 相当于obj只要 extend promise 上的方法,就成为了一个promise对象
// 返回当前Deferred对象的只读副本,或者为普通对象增加异步队列的功能
/**
* function aaa(){
* var dfd = $.Deferred();
* setTimeout(function(){
* dfd.resolve();
* }, 1000);
* // 返回promise在外部是修改不了状态的
* return dfd.promise();
* }
*/
promise: function( obj ) {
return obj != null ? jQuery.extend( obj, promise ) : promise;
}
},
deferred = {};
// Keep pipe for back-compat
/**
* var dfd = $.Deferred();
*
* setTimeout(function(){
* dfd.resolve("hi");
* }, 1000);
*
* // pipe的作用类似于:在dfd.resolve进行了一个传参,使用pipe可以进行对参数进行处理,创建出一个新的延迟对象(newDfd)
* // 再进行后续操作
* var newDfd = dfd.pipe(function(){
* return arguments[0] + '妙味';
* });
*
* newDfd.done(function(){
* alert(arguments[0]);
* });
*/
promise.pipe = promise.then;
// Add list-specific methods
/**
* tuples = [
// action, add listener, listener list, final state
[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
[ "notify", "progress", jQuery.Callbacks("memory") ]
]
*/
jQuery.each( tuples, function( i, tuple ) {
/**
* list是针对resolve reject notify状态的callbacks对象
* 就是当fire或者fireWith的时候,这些函数都会执行
* 如果是resolve,会添加[可以返回状态的函数, reject_list禁用, reject_notify锁]
* 如果是reject,会添加[可以返回状态的函数, resolve_list禁用, reject_notify锁]
* 如果是notify,啥也不会添加
*
* ted两种状态的列表添加预设的3个回调函数。第一个回调函数用于处理Deferred的状态(已成功或已失败),
* 第二个回调函数用于让另一个列表失效,这是因为当发布者发布内容的动作已经成功了,它就不可能再触发失败的状态,
* 因此让失败列表失效,防止给正在订阅了失败状态的函数被执行。反之亦然。然后锁定notify列表。
* 锁定notify只是让其的progressList的fire方法(即后面不能使用notify和notifyWith再进行推送内容了)不能被调用了。
* 但是,我们还可以给notify状态继续添加订阅者,这些订阅者会立即被执行。
*/
var list = tuple[ 2 ],
stateString = tuple[ 3 ];
// promise[ done | fail | progress ] = list.add
// promise 是提供方法的
promise[ tuple[1] ] = list.add;
// 如果状态是 resolved 或者 rejected
if ( stateString ) {
list.add(function() {
// state = [ resolved | rejected ]
state = stateString;
// [ reject_list | resolve_list ].disable; progress_list.lock
// i ^ 1 是 |i - 1| 的意思
// 意思就是如果是0 那么1禁用
// 如果是1 那么0禁用
}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
}
// deferred[ resolve | reject | notify ]
// 如果是resolve、reject、notify,执行fireWith
// 不添加with 是传入args deferred.reject(args)
deferred[ tuple[0] ] = function() {
deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
return this;
};
// deferred[ resolveWith | rejectWith | notifyWith ]
// 添加With 是传入context和args
// 一旦异步队列进入成功或失败状态,就会保持它的状态不变,例如,再次调用deferred.resolve()或deferred.reject()会被忽略
// 异步队列进入成功或失败状态后,仍然可以通过调用deferred.done()、deferred.fail()、deferred.then()、deferred.always()添加更多的回调函数,这些回调函数会被立即执行,并传入之前调用方法deferred.resolve(args)、deferred.resolveWith(context,args)、deferred.reject(args)或deferred.rejectWith(context,args)时传入的参数。
deferred[ tuple[0] + "With" ] = list.fireWith;
});
// Make the deferred a promise
// 将promise上的方法扩展到deferred身上
promise.promise( deferred );
// Call given func if any
if ( func ) {
func.call( deferred, deferred );
}
// All done!
return deferred;
},
}
- Deferred对象
- then方法图示:
jQuery中when源码分析
jQuery.extend({
// Deferred helper
/**
* 1. 不给when方法传入参数,那么when方法会返回一个状态是resolved的promise,所以then方法会马上执行
* $.when().then(function( x ) {
alert( "I fired immediately" );
});
2. 给when方法传入一个参数——(aaa() / "aaa")
3. 给when方法传入多个参数
function aaa(){
var dfd = $.Deferred();
dfd.resolve();
return dfd; // 执行完的结果必须是一个延迟对象,如果不返回延迟对象,就会忽略
}
function bbb(){
var dfd = $.Deferred();
dfd.resolve();
return dfd;
}
// 两个全成功才会走成功
// 只要有一个是失败,不管另一个是啥情况(成功、失败、没有),都会是失败
$.when(aaa(), bbb()).done(function(){
alert('成功');
}).fail(function(){
alert('失败');
});
——————————————————————————————————————————————————————————————————————————————
也可以这样传入多个参数:
(aaa(), 111, bbb(), 222)
4. 给resolve方法传入参数
var d1 = $.Deferred();
var d2 = $.Deferred();
var d3 = $.Deferred();
$.when( d1, d2, d3 ).done(function ( v1, v2, v3 ) {
alert( v1 ); // v1 is undefined
alert( v2 ); // v2 is "abc"
alert( v3 ); // v3 is an array [ 1, 2, 3, 4, 5 ]
});
d1.resolve();
d2.resolve( "abc" );
d3.resolve( 1, 2, 3, 4, 5 )
*/
when: function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
resolveValues = core_slice.call( arguments ), // [aaa(), bbb(), ccc()]
length = resolveValues.length,
// the count of uncompleted subordinates
/**
* 如果length == 1 (aaa()) / ("aaa")
* subordinate 是arguments中的第一个参数,也就是 aaa() / "aaa"
* 所以subordinate只有在length = 1的情况下才会被使用
*
* jQuery.isFunction( subordinate.promise ) 检验是否是Deferred对象
*
* remaining是Deferred对象的个数(也就是未完成的个数)
*/
remaining = (length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) )) ? length : 0,
// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
// 如果remaining === 1,说明是(aaa())这种类型,直接将aaa()返回就可以
// 如果是其它,就返回新建的Deferred对象
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
// Update function for both resolve and progress values
updateFunc = function( i, contexts, values ) {
/**
* 注意: 这里返回的是function( value ){}
* 在下面的函数里就是 done( updateFunc( i, resolveContexts, resolveValues ) );
* done( function(value){});
* 根据之前的介绍,可以知道value里传的是resolve里面的第一个参数,如果想要所有的参数,就是arguments
*/
return function( value ) {
// 这里的this指向的就是deferred对象
contexts[ i ] = this;
/**
* var d1 = $.Deferred();
var d2 = $.Deferred();
var d3 = $.Deferred();
$.when( d1, d2, d3 ).done(function ( v1, v2, v3 ) {
alert( v1 ); // v1 is undefined
alert( v2 ); // v2 is "abc"
alert( v3 ); // v3 is an array [ 1, 2, 3, 4, 5 ]
});
d1.resolve();
d2.resolve( "abc" );
d3.resolve( 1, 2, 3, 4, 5 )
如果是这样的话:value的值为:undefined / abc / 1(这里只包含了第一个参数)
arguments的值为:在下面(包含了所有的参数)
*/
values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
if( values === progressValues ) {
// 这里的values是在以后的函数中可能要用的,传入的是数组形式
// contexts 和 values 都可以为数组形式,因为数组里的每一个对应的是when里面的deferred状态
deferred.notifyWith( contexts, values );
// values 是 resolveValues
} else if ( !( --remaining ) ) {
// 当remaining为0的时候
deferred.resolveWith( contexts, values );
}
};
},
progressValues, progressContexts, resolveContexts;
// add listeners to Deferred subordinates; treat others as resolved
// (aaa(), 111, bbb(), 222)
// (aaa(), bbb(), ccc(), ddd())
if ( length > 1 ) {
progressValues = new Array( length ); // 储存 progress 的 deferred
progressContexts = new Array( length ); // 储存 progress 的 Contexts
resolveContexts = new Array( length ); // 储存 resovle 的 Contexts
for ( ; i < length; i++ ) {
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
// 给每种状态添加对应的方法
resolveValues[ i ].promise()
.done( updateFunc( i, resolveContexts, resolveValues ) )
.fail( deferred.reject ) // 只要有一个失败,就会触发返回的延迟对象deferred.reject
.progress( updateFunc( i, progressContexts, progressValues ) );
} else {
// 如果是111或222
--remaining;
}
}
}
// if we're not waiting on anything, resolve the master
// 不传参或者 remaining = 0(参数不为Deferred,为字符串之类),都走这里
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}
return deferred.promise();
}
});