jQuery异步框架探究3:jQuery.when方法

Posted 程序猿子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了jQuery异步框架探究3:jQuery.when方法相关的知识,希望对你有一定的参考价值。

(本篇文章针对jQuery1.6.1版本)经过前两篇文章对jQuery异步回调机制的详细分析,关于jQuery如何实现异步回调机制的原理已经非常清楚了--将"回调函数"与"击发动作"两个步骤分开,这样可以先把回调函数作为子弹预先存储到弹夹中,由指定对象在"特定条件"实现后再击发子弹。异步对象的done、fail、then、always方法都是存储回调函数的,异步对象的resolveWith、resolve、rejectWith、reject都是执行击发动作的,(特殊调用时序下也可以由前面四个方法执行空仓挂机复位射击操作)。本篇来详细讲讲"特定条件"有哪些。

1 window.setTimeout()方法触发


这个场景的"特定条件"是time out,也就是一个定时器经过设定的时间后触发"开枪射击",一个简单的例子即可说明。
	var gun = jQuery._Deferred();
	gun.done(function()
		console.info("time out,then execute this function! time is : ", new Date().getTime());
	);
	window.setTimeout(function()
		gun.resolve();
	, 5000);
	console.info("now time is : ", new Date().getTime());

当然,为了说明参数和特定对象,我们可以再设计一个简单的例子。

	var obj = 
		p1:function(a)console.info("obj.p1,a is '",a,"'");,
		p2:function(a,b)console.info("obj.p2,a is '",a,"', b is '", b, "'");
	;
	var gun2 = jQuery.Deferred();
	gun2.done(obj.p1, obj.p2, function(a,b)
		this.p1(a);
		this.p2(a,b);
	);
	window.setTimeout(function()
		gun2.resolveWith(obj, ["the article author is warhin.","who is warhin?","he is a diaosi!"]);
	, 5000);
	console.info("now time is : ", new Date().getTime());

2 浏览器加载文档dom对象树状态改变时触发


这个场景的"特定条件"是document状态改变,只要浏览器在加载dom过程中状态发生改变时(不限于文档加载完成状态)都可以触发"击发动作",一个超大的html文档或者用破网速访问一个破网站时示例会比较清晰,这里给出一个简单的例子。
	<html>
	<head>
	<script type="text/javascript" src="http://code.jquery.com/jquery-1.6.1.min.js"></script>
	<script type="text/javascript">
	(function()
		var gun = jQuery.Deferred();
		gun.done(function()
			var h1 = $("h1");
			if (h1[0]) 
				h1.bind("click", function()
					$("div").first().show();
				);
			
		);
		if ( document.addEventListener ) 
			document.addEventListener( "DOMContentLoaded", f, false );
		 else if ( document.attachEvent ) 
			document.attachEvent( "onreadystatechange", f );
		
		function f() 
			alert("document.readyState : "+document.readyState);
			gun.resolve();
		
	)();
	</script>
	</head>
	<body>
		<h1>点击我看看</h1>
		<div style='display:none'>其实我早就潜伏在这里了......</div>
	</body>
	</html>
jQuery与dom加载事件相关的模块就是用的jQuery异步回调机制,后面专门开辟文章详细分析。

3 长时间CPU计算结束或网络交互响应时触发


这个场景的"特定条件"是计算结束或者网络响应状态改变(失败、进行中、成功等),CPU计算的样例下面给出一个,后一种即ajax,关于ajax也单独讲解。
	var gun = jQuery.Deferred();
	gun.done(function()
		console.info("终于计算结束了");
	);
	(function()
		for (var i = 0; i <10000; i++) 
			(i+1) * (i+2);
		
		console.info("trigger");
		gun.resolve();
	)();

4 探究jQuery.when

	// Deferred helper
	when: function( firstParam ) 
		var args = arguments,
			i = 0,
			length = args.length,
			count = length,
			deferred = length <= 1 && firstParam && jQuery.isFunction( firstParam.promise ) ?
				firstParam :
				jQuery.Deferred();
		function resolveFunc( i ) 
			return function( value ) 
				args[ i ] = arguments.length > 1 ? sliceDeferred.call( arguments, 0 ) : value;
				if ( !( --count ) ) 
					// Strange bug in FF4:
					// Values changed onto the arguments object sometimes end up as undefined values
					// outside the $.when method. Cloning the object into a fresh array solves the issue
					deferred.resolveWith( deferred, sliceDeferred.call( args, 0 ) );
				
			;
		
		if ( length > 1 ) 
			for( ; i < length; i++ ) 
				if ( args[ i ] && jQuery.isFunction( args[ i ].promise ) ) 
					args[ i ].promise().then( resolveFunc(i), deferred.reject );
				 else 
					--count;
				
			
			if ( !count ) 
				deferred.resolveWith( deferred, args );
			
		 else if ( deferred !== firstParam ) 
			deferred.resolveWith( deferred, length ? [ firstParam ] : [] );
		
		return deferred.promise();
	
网上几乎大部分人对这个函数的讲解都不是很清晰,很多同学(包括所谓大神)的思维都是"按照我喜欢的想使用的方式调用这个方法",而不是"按照这个方法的api说明来遵守调用",自以为是的结果肯定是缘木求鱼、刻舟求剑,甚至通篇解释都跟这个方法的内部实现、参数使用和设计原理毫无关系。

如果用类似的实现来类比jQuery.when这个方法,那么java.util.concurrent.CyclicBarrier无疑是最贴切的。其思想简而言之就是"只有当所有线程都到达闸门时才触发开闸放水的动作",复杂言之就是这个方法接受两把或以上的"手枪"配合内部一把"手枪"一起使用(这把内部的枪将要导出其傀儡接受子弹),当传递的多把手枪都被击发后(根据各自不同的使用场景)才可以击发最后的那把枪。这也就是为什么这个方法名字要叫when并需要配合增强异步对象傀儡的then/done/fail/always方法一起使用的原因了:"when A1 and A2 [and A3...] then B",只有A1到An这一系列"特定条件"实现了才可以触发B的执行。

这里不继续展开其内部原理了(涉及到复杂的闭包等),只列举一个简单的例子说明(实际组合使用以及参数调用和传递情况可以很复杂)。
	var gun1 = jQuery.Deferred();
	gun1.done(function()
		console.info("g1");
	);
	var gun2 = jQuery.Deferred();
	gun2.done(function()
		console.info("g2");
	);
	var gun3 = jQuery.Deferred();
	gun3.done(function()
		console.info("g3");
	);
	jQuery.when(gun1,gun2,gun3).done(function()
		console.info('最后成功!');
	).fail(function()
		console.info('最后失败!');
	).always(function()
		console.info('总是执行!');
	);
	window.setTimeout(function()
		console.info("gun1 triggered!");
		gun1.resolve();
		window.setTimeout(function()
			console.info("gun2 triggered!");
			gun2.resolve();
		, 3000);
	, 5000);
	jQuery.ajax("http://www.zhihu.com/").always(function()
		console.info("gun3 triggered!");
		gun3.resolve();
	);

最后再纠正一个观点,严格来说javascript本身应该并没有真正的异步机制(在它的单线程执行环境下),但是对其回调机制可以非常灵活的实现,比如jQuery这样实现的,所以这几篇文章的标题其实应该是--jQuery回调机制探究。


重复申明:未经许可,严禁转载!


以上是关于jQuery异步框架探究3:jQuery.when方法的主要内容,如果未能解决你的问题,请参考以下文章

jQuery异步框架探究1:jQuery._Deferred方法

jQuery异步框架探究2:jQuery.Deferred方法

jQuery异步框架探究1:jQuery._Deferred方法

jQuery异步框架探究2:jQuery.Deferred方法

jQuery异步框架探究2:jQuery.Deferred方法

jQuery action类型实例方法探究:Array转换