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

(本文针对jQuery1.6.1版本号)关于Deferred函数的描写叙述中有一个词是fledged,意为"羽翼丰满的",说明jQuery.Deferred函数应用应该更成熟。

这个函数与jQuery._Deferred函数有密不可分的关系。

1 内部实现

	Deferred: function( func ) {
		var deferred = jQuery._Deferred(),
			failDeferred = jQuery._Deferred(),
			promise;
		// Add errorDeferred methods, then and promise
		jQuery.extend( deferred, {
			then: function( doneCallbacks, failCallbacks ) {
				deferred.done( doneCallbacks ).fail( failCallbacks );
				return this;
			},
			always: function() {
				return deferred.done.apply( deferred, arguments ).fail.apply( this, arguments );
			},
			fail: failDeferred.done,
			rejectWith: failDeferred.resolveWith,
			reject: failDeferred.resolve,
			isRejected: failDeferred.isResolved,
			pipe: function( fnDone, fnFail ) {
				return jQuery.Deferred(function( newDefer ) {
					jQuery.each( {
						done: [ fnDone, "resolve" ],
						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
			promise: function( obj ) {
				if ( obj == null ) {
					if ( promise ) {
						return promise;
					}
					promise = obj = {};
				}
				var i = promiseMethods.length;
				while( i-- ) {
					obj[ promiseMethods[i] ] = deferred[ promiseMethods[i] ];
				}
				return obj;
			}
		});
		// Make sure only one callback list will be used
		deferred.done( failDeferred.cancel ).fail( deferred.cancel );
		// Unexpose cancel
		delete deferred.cancel;
		// Call given func if any
		if ( func ) {
			func.call( deferred, deferred );
		}
		return deferred;
	},

事实上一张图就能非常好的说明调用jQuery.Deferred()函数之后返回的对象内部结构。

调用jQuery.Deferred()函数返回的也是一个异步对象,可是这个异步对象相比較jQuery._Deferred()返回对象是属于增强型的。这个增强的异步对象在内部很巧妙的使用了两个普通异步对象deferred和failDeferred,之所以不叫两个"异步队列",是由于那样会简单化这两个异步对象的真实结构--它们根本就不是一种数据结构,而是一系列特殊数据(闭包中的四个变量)和操作(五个方法)的集合,上一篇已具体展开描写叙述过。

这两个异步对象在jQuery.Deferred函数内初始化的时候全然同样:分别调用了一次jQuery._Deferred()函数而获得两个新创建的独立的异步对象,事实上这个时候它们并没有地位上的差异,就像一对孪生兄弟一样--身高一致。体重一致。可是接下来就厚此薄彼了--仅仅对当中一个对象deferred扩展属于还有一个对象failDeferred中的属性。扩展属性的一方deferred成为大哥。被共享属性免费提供服务的一方failDeferred成为小弟。

函数最后返回的引用仅仅有大哥deferred,大哥出人头地了。小弟被成为踏脚石永远暗无天日了(因为有大哥对他属性的引用。自己主动垃圾回收收不走他,真是想shi都无门的弟弟)。



从外部使用的角度看,仅仅有一个增强型异步对象deferred。可是这个对象具备了两个弹夹、两个扳机,这在实际生活中貌似还没有哪位设计师实现过这种武器(具有双管的枪型倒有不少),未来单兵武器系统--枪榴弹与袭击步枪合二为一的模型与此有部分相似。

通过扩展一套对立的操作增强一个普通异步对象到增强型异步对象的目的是添加很多其它的回调钩子应对很多其它的场景。这里通过第二个普通异步对象扩展了一套reject操作。其实,我们当然能够添加很多其它的普通异步对象扩展很多其它的操作,比方1.7版本号后又引入一套progess操作以说明场景不是仅仅有成功和失败,还有处理中呢。

Deferred函数内部运行了这样一句代码"deferred.done( failDeferred.cancel ).fail( deferred.cancel );",给增强的异步对象deferred提前提供两颗子弹,且这两个函数各自是兄弟俩的cancel函数,这将会达到一个相互排斥的效果--当调用方内部运行成功时能够触发射击deferred的弹夹callbacks,这个弹夹中的第一颗子弹(callbacks.shift())就是failDeferred对象的cancel方法,击发这颗子弹就锁死failDeferred对象了(failDeferred弹夹的其它子弹将不能被击发了);当调用方内部运行失败时能够触发射击failDeferred的弹夹callbacks,这个弹夹中的第一颗子弹(callbacks.shift())就是deferred对象的cancel方法,击发这颗子弹就锁死deferred对象了(deferred弹夹的其它子弹将不能被击发了)。之所以不说"一定"而是用"能够"这样的描写叙述,是由于两个弹夹能够互换使用的,仅仅是约定failDeferred对象的弹夹装失败场景下的触发函数集而已。

对增强型异步对象的扩展到这里貌似已经完美了,该有的很多其它的回调钩子都有了,可是等等,想想还有没有其它问题?第一篇文章讲过,普通异步对象的五个方法中有一个全能保险:cancelled和cancel方法,调用cancel方法后整个异步对象彻底锁死不能工作了。而增强型异步对象尽管没有引用弟弟的cancel操作,可是自己本身也有这种方法。所以为了避免让调用方任意关闭这个保险。jQuery从返回的哥哥对象中删除这个操作了"delete deferred.cancel;",这又相当于对哥哥的弱化处理(不能给它太多特权)。尽管外部不能调用这个操作了,可是增强型异步对象已经预先将cancel操作作为两颗子弹压入到弹仓了,这就是上一段代码的作用,另一个基础事实是删除的仅仅是引用,并没有删除cancel方法本身。

上面分析的都是jQuery.Deferred()这样的调用,这个函数也能够接受一个函数參数,这个參数函数仅仅被传入一个參数--增强的异步对象自身,至于函数内部怎么处理增强异步对象,全然由调用方自主决策--给了调用方在使用这把枪之前依据自己的喜好先设置一下它的机会,比方典型的初始化(能够在这里装入子弹)等,反正调用方已不能关闭它的保险了。



2 对jQuery._Deferred()扩展的promise()函数


这种方法须要单独讲讲。



前面说过对增强型异步对象有扩展增强也有收缩减弱。这个状态的增强型异步对象功能已经是完备的了。用于jQuery内部其它模块已经没有问题了。可是假设把这个异步对象直接交给不熟悉手枪操作的用户还是可能会引起误操作--尽管用户无法关闭保险,可是可能会在不恰当的时机运行"开枪射击"的操作(调用resolveWith方法或rejectWith方法)。射击的触发时机通常是有严格约束的。它往往与详细情境有关,比方浏览器载入dom仅仅有全然完毕时才干够触发相关指定事件运行,所以为了防止用户在错误的时间运行错误的事件,能够约束用户仅仅要他提供详细的回调函数就可以。实际的触发操作由jQuery自己决定,这样提供给用户的异步对象就不能有"开枪"的操作。为了实现这个目的,在增强型异步对象上扩展了一个promise方法,调用该方法返回增强型异步对象的一个傀儡,而不是增强型异步对象本身,这个傀儡仅仅同意下面操作集:'promiseMethods = "done fail isResolved isRejected promise then always pipe".split( " " )'。因为傀儡相应操作引用指向增强型异步对象相应同样的操作方法,所以设置这个傀儡的回调函数的同一时候对原增强型异步对象的回调函数的设置同一时候生效。

当然promise方法也能够接受一个參数。提供这个參数的话就把相关设置方法赋给这个參数对象,而不是给傀儡了。



补充一句,熟悉设计模式的同学一定能准确说出promise方法使用的是什么设计模式。

3 对jQuery._Deferred()扩展的pipe()函数


这个pipe方法是很挑战智力的一个方法。真正的"asynchronize"这个概念的实现就足够先进了(想想java中从bio到nio花了几个版本号和多长时间。从nio到aio又花了几个版本号和多长时间),再加上"pipe"这个概念也不是那么easy的(參照netty pipeline),所以彻底理解这种方法是相当费脑细胞的,因为兴许1.7以后的版本号中这种方法过时了,这里就不展开分析了。

最后总结一下,第一篇通过"手枪"模型详解了jQuery._Deferred函数的内部原理,本篇通过"未来单兵武器系统"模型详解jQuery.Deferred函数的内部原理,能够看到增强型异步对象本身并没有引入其它过多复杂的机制,仅仅是通过增强或减弱普通异步对象的功能来达到设计的目的。因此重点还是jQuery._Deferred函数。另外增强型异步对象尽管攻克了"添加很多其它的回调钩子应对不同场景下的回调逻辑"这个需求,可是jQuery整个异步机制还缺少最后一个环节--"什么时候由谁开枪"。更通俗的说是怎么应用于实际场景中。这个主题在下一篇解说jQuery.when函数时展开(上面对promise方法的解析中提到过一部分)。


最后再次郑重申明:本系列文章未经许可,严禁转载。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值