05异步队列 Deferred 使用介绍(2)


// Full fledged deferred (two callbacks list)
// 创建一个完整的异步队列(包含两个回调函数数组)
// 异步队列有三种状态:初始化(unresolved),成功(resolved),失败(rejected)。
// 执行哪些回调函数依赖于状态。
// 状态变为成功(resolved)或失败(rejected)后,将保持不变。
Deferred: function( func ) {
// _Deferred本无成功状态或失败状态,有四种状态:初始化、执行中、执行完毕、已取消
// 为了代码复用, 内部先实现了一个_Deferred
// failDeferred通过闭包引用
var deferred = jQuery._Deferred(),
failDeferred = jQuery._Deferred(),
promise;
// Add errorDeferred methods, then and promise
jQuery.extend( deferred, {
// 增加成功回调函数和失败回调函数到各自的队列中
// 便捷方法,两个参数可以是数组或null
// 状态为成功(resolved)时立即调用成功回调函数
// 状态为失败(rejected)时立即调用失败回调函数
then: function( doneCallbacks, failCallbacks ) {
// 上下文在这里有切换:虽然done返回的是deferred,但是fail指向failDeferred.done,执行fail是上下文变为failDeferred
// 简单点说就是:
// 调用done时向deferred添加回调函数doneCallbacks
// 调用fail时向failDeferred添加回调函数failCallbacks
// 因此这行表达式执行完后,返回的是failDeferred
deferred.done( doneCallbacks ).fail( failCallbacks );
// 强制返回deferred
return this;
},
// 注册一个callback函数,无论是resolved或者rejected都会被 调用。
// 其实,是把传入的函数(数组),同时添加到deferred和failDeferred
// 并没有像我想象的那样,存到单独的函数数组中
always: function() {
// done的上下文设置为deferred,fail的上下文设置为this
// done和fail的上下文不一致吗?一致!在这里this等于deferred
// 但是这里如此设置上下文应该该如何解释呢?与then的实现有什么不一样呢?
// fail指向fail指向failDeferred.done,默认上下文是failDeferred,failDeferred的回调函数数组callbacks是通过闭包引用的,
// 这里虽然将failDeferred.done方法的上下文设置为deferred,但是不影响failDeferred.done的执行,
// 在failDeferred.done的最后将this替换为deferred,实现链式调用,
// 即调用过程中没有丢失上下文this,可以继续链式调用其他的方法而不会导致this混乱
// 从语法上,always要达到的效果与then要达到的效果一致
// 因此,这行代码可以改写为两行(类似then的实现方式),效果是等价的:
// deferred.done( arguments ).fail( arguments );
// returnr this;
return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
},
// 增加失败回调函数
// 状态为失败(rejected)时立即调用
fail: failDeferred.done,
// 使用指定的上下文和参数执行失败回调函数队列
// 通过调用failDeferred.rejectWith()实现
rejectWith: failDeferred.resolveWith,
// 调用失败回调函数队列
// 通过调用failDeferred.resolve()实现
reject: failDeferred.resolve,
// 判断状态是否为成功(resolved)
isRejected: failDeferred.isResolved,
// 每次调用回调函数之前先调用传入的成功过滤函数或失败过滤函数,并将过滤函数的返回值作为回调函数的参数
// 最终返回一个只读视图(调用promise实现)
// fnDone在状态是否为成功(resolved)时被调用
// fnFail在状态是否为失败(rejected)时被调用
// 关于其他的解释:
// 1. 有的文章翻译为“管道机制”,从字面无法理解要表达什么含义,因此至少是不准确
// 2. 错误理解:所谓的pipe,只是把传入的fnDone和fnFail放到了成功队列和失败队列的数组头部
pipe: function( fnDone, fnFail ) {
return jQuery.Deferred(function( newDefer ) {
jQuery.each( {
done: [ fnDone, "resolve" ], // done在后文中会指向deferred.done
fail: [ fnFail, "reject" ]
}, function( handler, data ) {
var fn = data[ 0 ],
action = data[ 1 ],
returned;
if ( jQuery.isFunction( fn ) ) {
deferred[ handler ](function() {
returned = fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise().then( newDefer.resolve, newDefer.reject );
} else {
newDefer[ action ]( returned );
}
});
} else {
deferred[ handler ]( newDefer[ action ] );
}
});
}).promise();
},
// Get a promise for this deferred
// If obj is provided, the promise aspect is added to the object
// 返回的是一个不完整的Deferred的接口,没有resolve和reject,即不能 修改Deferred对象的状态,
// 这是为了不让外部函数提早触发回调函数,可以看作是一种只读视图。
//
// 比如$.ajax在1.5版本后不再返回XMLHttpRequest,而是返回一个封装了 XMLHttpRequest和Deferred对象接口的object。
// 其中Deferred部分就是promise()得到 的,这样不让外部函数调用resolve和reject,防止在ajax完成前触发回调函数。
// 把这两个函数的调用权限保留给ajax内部。
promise: function( obj ) {
if ( obj == null ) {
// 实际只会执行一次promise,第一次执行的结果被存储在promise变量中
if ( promise ) {
return promise;
}
promise = obj = {};
}
var i = promiseMethods.length;
// 又一种循环遍历方式
// 我习惯用:
// for( i = 0; i < len; i++ ) 或 for( i = len-1; i >=0; i-- ) 或 for( i = len; i--; )
// jQuery真是遍地是宝!
while( i-- ) {
obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
}
return obj;
}
});
// Make sure only one callback list will be used
// 成功队列执行完成后,会执行失败带列的取消方法
// 失败队列执行完成后,会执行成功队列的取消方法
// 确保只有一个函数队列会被执行,即要么执行成功队列,要么执行失败队列;
// 即状态只能是或成功、或失败,无交叉调用
// deferred和failDeferred的canceled属性,只能通过闭包引用,因此不用担心状态、上下文的混乱
deferred.done( failDeferred.cancel ).fail( deferred.cancel );
// Unexpose cancel
// 隐藏cancel接口,即无法从外部取消成功函数队列
delete deferred.cancel;
// Call given func if any
// 执行传入的func函数
if ( func ) {
func.call( deferred, deferred );
}
return deferred;
}


5.5 jQuery.when

复制代码 代码如下:

内容版权声明:除非注明,否则皆为本站原创文章。

转载注明出处:https://www.heiqu.com/wdjfjf.html