jQuery.when()方法测试笔记及源码分析

测试代码1:
如果传入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对象!

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值