jQuery源码学习(版本1.11)-Deferred

13 篇文章 0 订阅

概述

本分详细分析jquery-1.11.1.js源码文件行数:3251~3391;
代码简介:
使用jQuery.extend扩展了Deferred和when两个工具函数,其中when算是对Deferred的扩展应用;
Deferred函数返回一个Deferred对象,Deferred对象可分三种情况(resolve,reject,notify)分别保存各自的回调函数,这些回调会在Deferred状态变更的时候执行,不同的状态执行其各自的回调。可以说Deferred是管理异步回调的利器,可用于类似ajax请求,根据返回结果执行不同回调的场景;
When函数则是扩展Deferred,返回一个Deferred对象中的promise对象(只能添加回调,不能变更自身状态),这个对象可根据多个Deferred对象的状态去改变其状态;
下文对代码详细分析。

代码分析

// 扩展工具函数Deferred和when
jQuery.extend({

	Deferred: function( func ) {
		var tuples = [
				// 定义嵌套数组,内层数组里的项对应action, add listener, listener list, final state,每一个数组代表Deferred对象的一种状态
				[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
				[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
				[ "notify", "progress", jQuery.Callbacks("memory") ]
			],
			// 代表promise当前状态
			state = "pending",
			// 定义promise对象
			promise = {
				// 返回状态
				state: function() {
					return state;
				},
				// 往deferred添加回调,实际就是同时加进done跟fail的Callbacks对象里
				always: function() {
					deferred.done( arguments ).fail( arguments );
					return this;
				},
				// 一次性往deferred添加三种情况的回调
				// 并且可返回新deferred对象里的promise对象(不是直接返回deferred对象,这点很重要)
				// 返回的promise对象可以继续添加回调,但是只能根据原deferred的结果执行回调
				// 相当于依赖,生成一个子对象,依赖父对象的结果执行回调,延长了原deferred的作用范围
				then: function( /* fnDone, fnFail, fnProgress */ ) {
					// 缓存参数,以免后续作用域变更导致变量名冲突
					var fns = arguments;
					// promise()返回的是promise而不是deferred
					return jQuery.Deferred(function( newDefer ) {
						// 使用each将fns全部加进newDefer,并不是直接添加,可以看出外面还套了一层funcion
						jQuery.each( tuples, function( i, tuple ) {
							var fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
							// fn外面套了一层function才加进[ done | fail | progress ]三种场景的list中
							deferred[ tuple[1] ](function() {
								// 执行fn,得到返回值
								var returned = fn && fn.apply( this, arguments );
								// 返回值存在promise方法,说明是promise对象或deferred对象
								// 则将newDefer的触发方法[ done | fail | progress ] 加进去,returned又成为触发newDefer的另一个父对象,又必须触发returned才会触发newDefer
								if ( returned && jQuery.isFunction( returned.promise ) ) {
									returned.promise()
										.done( newDefer.resolve )
										.fail( newDefer.reject )
										.progress( newDefer.notify );
								// 否则直接触发newDefer已添加的回调
								} else {
									newDefer[ tuple[ 0 ] + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
								}
							});
						});
						fns = null;
					}).promise();
				},
				// 如果obj存在,使用jQuery.extend将promise扩展进去(即promise的属性会复制到obj中),否则直接返回promise对象
				promise: function( obj ) {
					return obj != null ? jQuery.extend( obj, promise ) : promise;
				}
			},
			// 定义deferred对象
			deferred = {};

		// Keep pipe for back-compat
		promise.pipe = promise.then;

		// 调用each方法,实际就是用deferred,对tuples里的数组(三个Callbacks对象)进行包装的过程
		jQuery.each( tuples, function( i, tuple ) {
			// list获得的是一个Callbacks对象
			var list = tuple[ 2 ],
				stateString = tuple[ 3 ];

			// 引用Callbacks对象的add方法包装起来,promise[ done | fail | progress ] = list.add
			// promise对象里引用了[ done | fail | progress ] 三种方法,但没有[ resolve | reject | notify ]方法
			// 表明promise无法触发回调
			promise[ tuple[1] ] = list.add;

			if ( stateString ) {
				// 往list添加方法,由Callbacks对象的源码可知,这些方法会在list调用fire的时候执行
				list.add(function() {
					// 设置状态,只会出现两种情况state = [ resolved | rejected ]
					state = stateString;

				// resolved和rejected对对立的,异或取反添加对应Callbacks对象的disable方法,让其执行
				// 同时添加tuples[ 2 ][ 2 ].lock,即notify的方法
				}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
			}

			// 包装方法deferred[ resolve | reject | notify ]
			deferred[ tuple[0] ] = function() {
				// 实际就是调用Callbacks的fireWith方法,这里传递了上下文,可知最终执行方法的上下文就是promise或this
				deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
				return this;
			};
			// 包装Callbacks的fireWith方法
			deferred[ tuple[0] + "With" ] = list.fireWith;
		});

		// 将promise对象扩展进deferred对象
		promise.promise( deferred );

		// 创建对象的时候如果传入了fun参数,执行之
		if ( func ) {
			func.call( deferred, deferred );
		}

		// 返回deferred
		return deferred;
	},

	// Deferred helper 接收多个Deferred对象作为参数,返回一个新Deferred对象的promise,新对象的状态由所有参数共同控制
	when: function( subordinate /* , ..., subordinateN */ ) {
		var i = 0,
			resolveValues = slice.call( arguments ),
			length = resolveValues.length,

			// 获取传入的Deferred对象个数,如果只有一个,则取0
			remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,

			// 最终返回deferred.promise(),如果不传参,为0或大于1,则新建一个Deferred对象,否则直接为第一个参数
			deferred = remaining === 1 ? subordinate : jQuery.Deferred(),

			// 当参数Deferred对象状态变化时,更新deferred状态
			updateFunc = function( i, contexts, values ) {
				// 实际是返回一个function到参数Deferred对象的list中,这样该Deferred对象状态变化时就会触发回调更新deferred状态
				return function( value ) {
					// 保存上下文,实际就是每次调用时的其中一个参数Deferred对象
					contexts[ i ] = this;
					// 保存该参数Deferred对象执行回调时传的参数
					values[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
					// 直接判断values是哪个数组对象可知当前执行的参数Deferred是哪个状态
					// deferred.notifyWith每当一个参数Deferred执行一次notify时都会执行
					if ( values === progressValues ) {
						deferred.notifyWith( contexts, values );

					// 只有当所有参数Deferred对象的状态都是resolved时才会调用deferred.resolveWith
					// 此时contexts即为resolveContexts已经存下了所有的参数Deferred对象,作为deferred里回调的参数
					} else if ( !(--remaining) ) {
						deferred.resolveWith( contexts, values );
					}
				};
			},
			
			// 定义三个数组,用于保存参数Deferred对象执行回调时的参数和上下文,作为deferred里回调的参数
			progressValues, progressContexts, resolveContexts;

		// 判断length长度,对每一个参数Deferred对象都使用done,fail,progress添加上updateFunc的返回值(即上述分析的一个更新deferred状态回调函数)
		// 这样每个参数Deferred对象状态变更时,都会更新deferred的状态,实现对deferred的控制
		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;
				}
			}
		}

		// remaining为0则在创建过程中直接就调用resolveWith,状态就会直接变为resolved
		if ( !remaining ) {
			deferred.resolveWith( resolveContexts, resolveValues );
		}

		// 最终只返回promise对象,这样就只能添加回调,而无法自己触发回调
		return deferred.promise();
	}
});


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值