首页 > 技术文章 > jquery deferred对象解析

osinger 2016-10-31 11:37 原文

建议先看下callbacks对象的实现
Deferred 是callbacks 方法的封装调用。开启三个队列实例,分别是resolve,reject,notify。将每个队列开启list.add方法,并且分别指向自己的done、fail、progress

触发 resolve,reject方法,实际上指向的是callbacks的 fireWith方法,直接触发了队列。所以先使用then方法,加入队列中,然后使用resolve, reject来进行队列的触发,关闭等操作~
简单调用方法

var d = new Deferred();

d.then(function(
  console.info('resolve done');
),function(){
  console.info('reject fail');
},funciton() {
  console.info('notify progress);
});
// ====不能同时触发===可以单个执行===
// 直接触发done
d.resolve();
// fail
d.reject();
// pregress
d.notify();

deferred的中文注解

jQuery.extend({

	Deferred: function( func ) {
		var tuples = [
				// once more 保证添加到队列的,只执行一次,并且一旦add立即触发
				// action, add listener, listener list, final state
				[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
				[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
				[ "notify", "progress", jQuery.Callbacks("memory") ]
			],
			state = "pending",
			promise = {
				state: function() {
					return state;
				},
				always: function() {
					deferred.done( arguments ).fail( arguments );
					return this;
				},
				then: function( /* fnDone, fnFail, fnProgress */ ) {
					var fns = arguments;
					// jQuery.Defered(func()).promise();
					return jQuery.Deferred(function( newDefer ) {
						jQuery.each( tuples, function( i, tuple ) {
							// resolve/reject/notify
							var action = tuple[ 0 ],
							// 如果没有fnsi 那么久违undefined
								fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
							// deferred[ done | fail | progress ] for forwarding actions to newDefer
							// deferred['done']() | deferred['fail']() |deferred['progress']() 在下面有定义
							// 实际上这里的deferred done 指向的是list.add,将done,fail,progress的方法都进行了队列添加
							deferred[ tuple[1] ](function() {
								// 直接调用fn方法,得到返回结果(这个returned是deferred对象么?为什么可以调用returned.promise)
								var returned = fn && fn.apply( this, arguments );

								if ( returned && jQuery.isFunction( returned.promise ) ) {
									// 将方法扩展到deferred对象实例,然后执行done,fail,progress方法
									// 实际上是进行了callbacks的add调用,不过进行了区分顺序
									returned.promise()
										.done( newDefer.resolve )
										.fail( newDefer.reject )
										.progress( newDefer.notify );
								} else {
									newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
								}
							});
						});
						fns = null;
					}).promise();
				},
				// Get a promise for this deferred
				// If obj is provided, the promise aspect is added to the object
				// 将promise和obj进行合并
				promise: function( obj ) {
					return obj != null ? jQuery.extend( obj, promise ) : promise;
				}
			},
			deferred = {};

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

		// Add list-specific methods
		// 建立resolve,reject,notify的队列实例,并且对done,fail,progress指向list.add 使其可以对自己的队列进行添加方法
		jQuery.each( tuples, function( i, tuple ) {
			// 队列实例
			var list = tuple[ 2 ],
				stateString = tuple[ 3 ];

			// promise[ done | fail | progress ] = list.add
			// 将队列实例的add方法赋值给promise的 resolve/reject/notify
			// 引用add方法
			promise[ tuple[1] ] = list.add;

			// Handle state
			if ( stateString ) {
				list.add(function() {
					// state = [ resolved | rejected ]
					state = stateString;

				// [ reject_list | resolve_list ].disable; progress_list.lock
				// 执行了异或运算,将列表置为disable,将最后的notify  lock
				// 0 ^ 1 = 1 
				// 1 ^ 1 = 0
 				}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
			}

			// deferred[ resolve | reject | notify ]
			// 组装deferred对象 添加 resolve,reject,notify属性方法
			deferred[ tuple[0] ] = function() {
				deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
				return this;
			};
			// 组装deferred对象 添加resolveWith,rejectWith,notifyWith对象指向callbacks的FireWire方法
			// fireWith 是用来拼接list的方法~
			deferred[ tuple[0] + "With" ] = list.fireWith;
		});

		// Make the deferred a promise
		// 组装deferred对象 将promise合并到deferred
		promise.promise( deferred );

		// Call given func if any
		//  直接调用该方法~
		if ( func ) {
			func.call( deferred, deferred );
		}

		// All done!
		// deferred 对象包含了 resolve,reject,notify,resolveWith,rejectWith,notifyWith,pipe,done,fail,progress,state,always,then,promise
		return deferred;
	},

	// Deferred helper
	when: function( subordinate /* , ..., subordinateN */ ) {
		var i = 0,
			resolveValues = core_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 ) {
					contexts[ i ] = this;
					values[ i ] = arguments.length > 1 ? core_slice.call( arguments ) : value;
					if( values === progressValues ) {
						deferred.notifyWith( contexts, values );
					} else if ( !( --remaining ) ) {
						deferred.resolveWith( contexts, values );
					}
				};
			},

			progressValues, progressContexts, resolveContexts;

		// add listeners to Deferred subordinates; treat others as resolved
		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;
				}
			}
		}

		// if we're not waiting on anything, resolve the master
		if ( !remaining ) {
			deferred.resolveWith( resolveContexts, resolveValues );
		}

		return deferred.promise();
	}
});

推荐阅读