如果传入when的不是Deferred也不是Promise对象,那么马上执行done方法,done方法会被传入初始参数也就是传入when方法的参数
同时fail方法永远不会被执行,因为当传入的不是Deferred也不是Promise时候,会被当作resolved所以不会是rejected
$.when( { testing: 123 } ).done(function( x ) {
alert( x.testing ); // Alerts "123"
});
测试代码2:
如果不给when方法传入参数,那么when方法会返回一个状态是resolved的promise,所以then方法会马上执行!
$.when().then(function( x ) {
alert( "I fired immediately" );
});
测试代码3:
当给when方法传入多个Deferred对象时候,那么when方法返回一个新的Deferred,该Deferred能够检测传入的多个Deferred的状态
当多个Deferred都是resolved才会调用done方法,只要有一个Deferred是rejected就会调用fail方法。
传给done方法的参数就是传给每一个Deferred的resolve方法的参数,而且顺序就是传给when方法的Deferred的顺序!
var d1 = $.Deferred();
var d2 = $.Deferred();
$.when( d1, d2 ).done(function ( v1, v2 ) {
alert( v1 ); // "Fish"
alert( v2 ); // "Pizza"
});
d1.resolve( "Fish" );
d2.resolve( "Pizza" );
测试代码4:
如果一个Deferred的resolve方法没有传入参数,那么回调函数的done方法的相应参数就是undefined
如果一个Deferred的resolve方法传入的是单个参数,那么回调函数的done方法的相应参数就是该单个参数的值
如果一个Deferred的resolve方法传入的是多个参数,那么回调函数的done方法的相应参数就是多个参数组成的数组!
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 )
测试代码5:
当有多个Deferred作为参数的时候只要有一个Deferred被reject那么就会马上调用fail回调函数,注意:这时候
可能其它的Deferred还没有resolve调用。这时候如果你要取消一些未完成的请求,例如ajax请求,那么你可以保存 在闭包中的jqXHR对象,然后在fail回调函数中取消。success方法:该函数有3个参数:请求返回的数据、响应状态字符串、jqXHR对象。error方法:指定请求失败时执行的回调函数。该函数有3个参数:jqXHR对象、 请求状态字符串(null、 'timeout'、 'error'、 'abort'和'parsererror')、 错误信息字符串(响应状态的文本描述部 分,例如'Not Found'或'Internal Server Error')。这是一个Ajax事件。跨域脚本和跨域JSONP请求不会调用该函数。
$.when( $.ajax( "/page1.php" ), $.ajax( "/page2.php" ) ).done(function( a1, a2 ) {
var data = a1[ 0 ] + a2[ 0 ]; // a1[ 0 ] = "Whip", a2[ 0 ] = " It"
//a1和a2是page1和page2的ajax请求时候调用resolve时候的参数,每一个参数是一个数组,数组签名是[data,statusText,jqXHR]
if ( /Whip It/.test( data ) ) {
alert( "We got what we came for!" );
}
});
//下面的调用方式表示,如果两者都成功那么调用myFunc函数,否则调用myFailure函数
$.when( $.ajax( "/page1.php" ), $.ajax( "/page2.php" ) )
.then( myFunc, myFailure );
原文链接:点击打开链接
测试用例1(把数组传入其它函数用于修改):
var arr=[undefined,undefined];
func1(arr);
func2(arr);
alert("outcome->"+arr);//打印[Fish,Pizza]
function func1(array)
{
array[0]="Fish";
}
function func2(array)
{
array[1]="Pizza";
}
note:在when函数的源码中就用到了用数组对象来判断是notify还是resolve调用类型的方式!
测试用例2:
$.when1(d1, d2).progress(function(val){alert("progress"+val)});
d1.notify("aa");//打印"progressaa"
d2.notify("bb");//打印"progressaa"
note:notifyWith在Callbacks对象里面是用memory构造的,所以会记住上一次的参数结果,当第一次notify("aa")那么aa会被记忆,
下次再次notify的时候还是用旧的参数调用新的函数!在
Callbacks里面不是说过,会经过两个步骤吗,第一个步骤是用旧参数调用新函数,第二个步骤是用新参数调用所有的函数?解释:请不要把Callbacks和when方法混淆,虽然他们底层思想是一样的!这里牵涉到的Callbacks只有progress对应的Callbacks,他会记忆上一次调用的参数没有错,理解的很对,但是他的参数是when给的,在里面调用deferred.notifyWith( contexts, values );其中values是一个数组,元素1是第一个Deferred的参数,元素2是第二个Deferred调用时候的参数,如果把代码修改成为下面这样:(要用形参去接收)
var d1 = $.Deferred();
var d2 = $.Deferred();
//第一种情况:resolve调用
$.when1(d1, d2).progress(function(val,val2){alert(val2+"progress"+val)});
d1.notify("aa");//undefinedprogressaa
d2.notify("bb");//bbprogressaa
$.when源码分析:
when: function( subordinate /* , ..., subordinateN */ ) {
var i = 0,
resolveValues = slice.call( arguments ),
length = resolveValues.length,
// the count of uncompleted subordinates
remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,
// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
// Update function for both resolve and progress values
updateFunc = function( i, contexts, values ) {
return function( value ) {
//这里的value就是resolve等方法传入的参数!如果调用的是done方法,那么就会把resolveContext传递过来
//这个context里面会放入when方法传入的参数,也就是多个Deferred对象
contexts[ i ] = this;
values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
//除非外面调用的notify否则不可能相等,因为只有progress会把progressValue传递进来,从而数组引用相同,否则不可能相等!
//如果不相等就表示调用的是resolve,如果相等那么就表示调用的是notify!这种用数组相等来判断是那种调用的方法极其巧妙!
if ( values === progressValues ) {
deferred.notifyWith( contexts, values );
//如果不想等那么表示是resolve那么把remiaining减少,如果没有减少到0,那么就不会调用外层的Deferred对象的resolveWith方法
//只有减少为0的时候才会调用外层的resolveWith方法!
} else if ( !(--remaining) ) {
//(1)这里就可以说明上面的测试代码4的情况,为什么每一个Deferred对象resolve的参数参数会被传递到总的Deferred对象的done
//方法或者progress方法中,因为所有Deferred对象的回调函数updateFunc()会把参数传递到values[i]中,把每次传递的一个参数
//作为这个数组的一个元素,于是最后这个数组就会存在,当总的Deferred已经resolve,也就是remaining是0的时候就会
//调用他,底层调用的是Callbacks对象的fireWith
//(2)至于上面的if语句,每一个Deferred对象调用notify那么if语句总是满足的,于是都会调用notifyWith.同时要记住:
//notifyWith在Callbacks对象里面是用memory构造的,所以会记住上一次的参数结果,当第一次notify("aa")那么aa会被记忆
//下次再次notify的时候还是用旧的参数调用新的函数!
deferred.resolveWith( contexts, values );
}
};
},
progressValues, progressContexts, resolveContexts;
// add listeners to Deferred subordinates; treat others as resolved
if ( length > 1 ) {
//length是几,那么创建的数组长度就是几!
//有什么作用?
//(1)在updateFunc用数组的引用判断是那种调用类型,对于notify类型,那么调用progress那么传入的参数是progressContexts,
//progressValues于是在updateFunc中values就是progressValues,也就是两者相等那么表示要调用总的Deferred对象的progress方法!
//于是调用nodifyWith方法,上下文就是Deferred对象,第二个参数就是values[i]也就是notify函数调用时候传递的参数,如dfd.notify("xxx")
//那么values[i]="xxx",这个"xxx"会传入到when函数的progress方法中作为参数!
//(2)在updateFunc中会用这些数组保存所有的notify或者resolve或者reject调用时候的参数,从而把这个参数数组封装到done,fail,progress回调函数中!
progressValues = new Array( length );
progressContexts = new Array( length );
resolveContexts = new Array( length );
for ( ; i < length; i++ ) {
//如果不是Deferred对象那么直接把remaining长度减一!
if ( resolveValues[ i ] && jQuery.isFunction( resolveValues[ i ].promise ) ) {
resolveValues[ i ].promise()
//resolveValues[ i ].promise()就是arguments[0]或者arguments[1]
//done就是arguments[i]的done方法!也就是给这个arguments[i]对应的Callbacks中添加一个回调函数,当这个when参数,也就是传入的Deferred对象
//resolve以后就会回调这个函数,回调这个函数就是去执行updateFunc函数,在这个函数里面对remaining进行修改!
.done( updateFunc( i, resolveContexts, resolveValues ) )
//只要有一个未完成,那么就触发新创建的deferred的reject方法,进而调用when后面的fail方法!
.fail( deferred.reject )
.progress( updateFunc( i, progressContexts, progressValues ) );
} else {
--remaining;
}
}
}
// if we're not waiting on anything, resolve the master
if ( !remaining ) {
deferred.resolveWith( resolveContexts, resolveValues );
}
return deferred.promise();
}
总结:
(1)相比于Deferred对象,该when方法可以运用于多个延迟对象!
(2)when方法的参数必须是延迟对象,如果不是延迟对象那么when函数会直接跳过这个函数,不会判断它成功与否!其它的函数成功他就成功!
所以如果参数不是Defered对象那么唯一的作用就是会把when里面的参数作为参数传递给done函数,done函数中通过arguments[0],arguments[1]访问!(3)当没有传入参数的时候那么这时候在内部就会创建一个Deferred对象,然后remaining是0,调用该创建的对象的resolveWith方法!进而调用done方法!
(4)传入一个参数的时候这个deferred就是第一个参数,如果第一个参数resolve那么就会马上调用done方法,因为公用了同一个deferred对象!
(5)如果传入的参数不是Deferred那么创建一个新的Deferred对象然后remianing是0,于是调用该deferred的resolveWith,同时传入resolveValues数组,也就是会把数组传入done方法!
(6)要知道源码在for循环中用到了done(updateFunc(i,resolveContexts,resolveValues))其中updateFunc调用后其实返回的是一个匿名函数,该函数会接受一个参数。其实在done方法中调用的jQuery.Callbacks的add方法,也就是往这个Callbacks中添加了函数,构成了一个list数组,然后在resolve的时候其实是调用了Callbacks对象的fireWith,在fire中传入的参数会被原封不动的用于去调用list中的所有的函数,这里的逻辑就是一样的,会拿着fire中的参数去调用done中的所有的函数,所以updateFunc返回的匿名函数的参数value就是resolve调用时候的参数值!
(7)contexts[ i ] = this;其中的this就是Deferred对象,可以通过打印this.done等知道!至于为什么是Deferred对象,原因就是done方法最后还是Deferred对象调用的,所以this就是指向Deferred对象!