jQuery提供的when方法可以管理多個Deferred對象。打比方說如果你創(chuàng)建了多個Deferred對象,這個時候你想等所有Deferred調(diào)用完畢之后執(zhí)行一段代碼,這個時候我們就可以使用when來實(shí)現(xiàn)。
源碼
/**源碼3089行**/
when: function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
resolveValues = core_slice.call( arguments ),
length = resolveValues.length,
/**源碼3095行**/
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
/**源碼3098行**/
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
// Update function for both resolve and progress values
updateFunc = function( i, contexts, values ) {
return function( value ) {
contexts[ i ] = this;
values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
if( values === progressValues ) {
deferred.notifyWith( contexts, values );
} else if ( !( --remaining ) ) {
deferred.resolveWith( contexts, values );
}
};
},
progressValues, progressContexts, resolveContexts;
/**源碼3116行**/
if ( length > 1 ) {
progressValues = new Array( length );
progressContexts = new Array( length );
resolveContexts = new Array( length );
for ( ; i < length; i++ ) {
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
resolveValues[ i ].promise()
.done( updateFunc( i, resolveContexts, resolveValues ) )
.fail( deferred.reject )
.progress( updateFunc( i, progressContexts, progressValues ) );
} else {
--remaining;
}
}
}
/**源碼3133行**/
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}
return deferred.promise();
}
原理
when的原理:
利用remaining來記錄Deferred的數(shù)量,如果傳入的不是Deferred對象,則不記錄(源碼中直接進(jìn)行remaining-1操作)。當(dāng)Deferred執(zhí)行成功之后會將remaining進(jìn)行-1操作,當(dāng)最后一個Deferred執(zhí)行完畢(成功)之后(remaining等于0的時候)這個時候我們就會觸發(fā)when方法里面創(chuàng)建的Deferred對象。如果遇到其中一個Deferred調(diào)用了失敗方法,則直接調(diào)用when的reject。如果傳入的參數(shù)沒有一個是Deferred對象,則直接調(diào)用回調(diào)函數(shù)。
解釋
首先來看源碼3095行和源碼3098行,這里的意思是值如果傳入的參數(shù)如果只有一個并且是Deferred對象,則直接使用這個Deferred對象,如果傳入的這個參數(shù)不是Deferred對象或者存在多個參數(shù)則直接創(chuàng)建一個新的Deferred對象。
源碼3116行判斷如果參數(shù)個數(shù)大于1個的時候,這個時候我們首先來循環(huán)這些參數(shù)判斷是否是Deferred對象,如果不是則將remaining直接進(jìn)行-1操作,如果是Deferred對象,這個時候我們可以看到done和progress都調(diào)用了updateFunc函數(shù)。在updateFunc函數(shù)里面進(jìn)行了判斷如果第三個參數(shù)是progressValues則直接調(diào)用我們創(chuàng)建的deferred的notifyWith,所以綁定在when后面的progress可能會觸發(fā)多次根據(jù)參數(shù)中Deferred對象調(diào)用progress的次數(shù)而定。如果是done觸發(fā)的
updateFunc方法,這個時候我們會先將remaining進(jìn)行-1操作,如果remaining等于0的時候則直接觸發(fā)deferred的resolveWith。
源碼3133行的意思就是 如果我們傳入的參數(shù)全都不是Deferred對象,這個時候我們就直接觸發(fā)成功的方法。最后return deferred.promise();不給外部提供修改狀態(tài)的方法。
用法
var def1 = $.Deferred();
setTimeout(function(){
def1.resolve();
}, 1000);
$.when(def1).done(function(){
console.log('全部成功');
}).fail(function(){
console.log('有失敗');
})
//1秒中之后會調(diào)用成功