Skip to content

Latest commit

 

History

History
676 lines (525 loc) · 15.6 KB

延迟对象.md

File metadata and controls

676 lines (525 loc) · 15.6 KB

8. 延迟对象

5. 工具方法类似,都是在JQuery对象上添加新的属性方法

jQuery.extend({
	Deferred: function(){},  # 延迟对象
	when:function(){}        # 延迟对象辅助方法
})

8.1 $.Deffered()

源码

//[3043]
jQuery.extend({

	Deferred: function( func ) {
		var tuples = [
				// 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 */ ) {
					// then(function(){},function(){},function(){})
					// 所以arguments是的属性是三个函数
					// 利用fns保存arguments参数
					var fns = arguments;
					// return jQuery.Deffered(fn).promise()
					// 根据if ( func ) {func.call( deferred, deferred )}
					// 因此newDefer就是deffered对象
					// 且this指向了deffered对象
					// 并立马执行了func
					return jQuery.Deferred(function( newDefer ) {
						jQuery.each( tuples, function( i, tuple ) {
							// action : resolve reject notify
							var action = tuple[ 0 ],
								// 获取then()中对应三种状态的函数
								fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];
							// deferred[ done | fail | progress ] for forwarding actions to newDefer
							// deffered.done(fn) deffered.fail(fn) deffered.progress(fn)
							deferred[ tuple[1] ](function() {
								// 当resolve/reject/notify执行的时候
								// done/fail/progress就会触发,因此就可以执行then中对应的函数
								var returned = fn && fn.apply( this, arguments );
								// 如果then(function(){return})
								// 匿名函数中有返回值
								// 如果返回值是deffered对象
								// 详见(五)
								if ( returned && jQuery.isFunction( returned.promise ) ) {
									returned.promise()
										.done( newDefer.resolve )
										.fail( newDefer.reject )
										.progress( newDefer.notify );
								} else {
									//详见(五)
									//如果返回值不是deffered对象
									//直接fireWith 可以触发done函数
									//需要注意的是newDefer和返回值dfd是怎么建立关系的,就是通过闭包的形式,将之前保留的deffered对象再次传入$.Deffered(fun)的fun中传入
									newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );
								}
							});
						});
						fns = null;
					}).promise();
				},

				//详见(二)
				//有参数的时候例如后面传入deffered
				//则将promise对象扩展到deffered对象
				//如果没有参数传入,则就返回promise对象本身
				//例如$.Deffered().promise()
				//返回的是promise对象而不是deffered对象
				promise: function( obj ) {
					return obj != null ? jQuery.extend( obj, promise ) : promise;
				}
			},

			//注意闭包形式,因为外部调用$.Deffered()会一直保持着 引用
			//所以这个对象暂时是不会释放的
			//这个对象有很多属性是函数
			//相当于返回了这些函数,因此返回函数内部的嵌套函数就属于闭包形式
			deferred = {};

		// Keep pipe for back-compat
		// 两个函数每种形式上是通用的
		promise.pipe = promise.then;

		// Add list-specific methods
		// 其实这里就相当于添加add和fire函数
		// 需要注意的是和tuples数组是对应起来的
		// 例如done对应 add
		// 那么resolve就对应 fireWith
		jQuery.each( tuples, function( i, tuple ) {
			// list 就是$.Callback()
			// 每一种状态就有一个Callback
			var list = tuple[ 2 ],
				stateString = tuple[ 3 ];

			// promise[ done | fail | progress ] = list.add
			// 因为memory所以直接add就fire了?
			promise[ tuple[1] ] = list.add;

			// Handle state
			// notify是没有stateString
			// 只有resolve和reject才会执行
			if ( stateString ) {
				// 因为memory这里先添加add?
				// 这里状态是不能被改变的
				// 在执行任何一种状态的时候另外的状态都会被锁定
				list.add(function() {
					// state = [ resolved | rejected ]
					// 详见(三)
					state = stateString;

				// [ reject_list | resolve_list ].disable; progress_list.lock
				}, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );
			}

			// deferred[ resolve | reject | notify ]
			// 需要注意这里后执行
			// 这里只有在外部调用 resolve reject等函数时才会执行
			// 后面的先执行所以deferred[ tuple[0] + "With" ]存在
			deferred[ tuple[0] ] = function() {
				// resoveWith rejectWith notifyWith
				deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );
				return this;
			};
			// 这里先执行
			deferred[ tuple[0] + "With" ] = list.fireWith;
		});

		// Make the deferred a promise
		// 详见(二)
		// 使deffered对象继承promise对象
		promise.promise( deferred );

		// Call given func if any
		// 这一这个可以在外部使用,内部详见then方法
		if ( func ) {
			func.call( deferred, deferred );
		}

		// All done!
		// 调用$.Defferd()返回的是deffered对象
		// 闭包形式
		return deferred;
	}
});

内容解析

(一) 案例说明

延迟对象其实是对回调对象的再次封装.

var $callback = $.Callbacks('memory once');
var $deferred = $.Deferred();

function fn1() {
    console.log('callback fn1');
}

function fn2() {
    console.log('deferred fn2');
}

setTimeout(function() {
    console.log('defer');   //defer
    $callback.fire();       //callback fn1
    $deferred.resolve();    //deferred fn2
},1000);

$callback.add(fn1);
$deferred.done(fn2);


//add -> done
//fire -> resolve
//Callbacks -> Deferred

延迟对象的resolvereject对应oncememory参数

//[ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],
//[ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],
//[ "notify", "progress", jQuery.Callbacks("memory") ]

var $callback = $.Callbacks('memory once');
var $deferred = $.Deferred();

function fn1() {
    console.log('callback fn1');
}

function fn2() {
    console.log('deferred fn2');
}


setInterval(function() {
    console.log('defer');   //defer N次
    $callback.fire();       //callback fn1 只有一次 因为参数once
    $deferred.resolve();    //deferred fn2 只有一次 因为参数once
},1000);

$callback.add(fn1);
$deferred.done(fn2);

延迟对象的notify没有once参数

var $callback = $.Callbacks('memory once');
var $deferred = $.Deferred();

function fn1() {
    console.log('callback fn1');
}

function fn2() {
    console.log('deferred fn2');
}


setInterval(function() {
    console.log('defer');   //defer N次
    $callback.fire();       //callback fn1 只有一次 因为参数once
    $deferred.notify();    //deferred fn2 N次 因为没有参数once
},1000);

$callback.add(fn1);
$deferred.progress(fn2);

延迟对象的notify只对应memory参数

var $callback = $.Callbacks('memory once');
var $deferred = $.Deferred();

function fn1() {
    console.log('callback fn1');
}

function fn2() {
    console.log('deferred fn2');
}

$callback.add(fn1);
$deferred.progress(fn2);

$deferred.notify();      //deferred fn2
$deferred.progress(fn2); //deferred fn2 因为memory 直接fire
$deferred.progress(fn2); //deferred fn2 因为memory 直接fire

(二) promisedeffered对象的区别

  • promise(使用promise对象不可以修改外部状态)

  • state

  • always

  • promise

  • pipe

  • then

  • done

  • fail

  • progress

  • deffered(多了三个状态,使用deffered可以修改状态)

  • resolve

  • resolveWith

  • reject

  • rejectWith

  • notify

  • notifyWith

  • state(这之后都是从promise对象继承而来)

  • always

  • promise

  • pipe

  • then

  • done

  • fail

  • progress

使用deffered对象可以在外部修改内部状态

function fn() {
    var dfd = $.Deferred();

    setTimeout(function() {
        dfd.resolve();  //因为先reject所以状态被改变
    },1000);

    return dfd;
}

var dfd = fn();

dfd.done(function() {
    console.log('success');
}).fail(function() {
    console.log('fail');  //fail
});

dfd.reject();	//失败,说明在外面可以改变状态,因为用的是deffered对象

使用promise对象不可以在外部修改内部状态

function fn() {
    var dfd = $.Deferred();

    setTimeout(function() {
        dfd.resolve();  //内部resolve状态不能被外部的reject修改
    },1000);

    return dfd.promise();
}

var dfd = fn();

dfd.done(function() {
    console.log('success');  //success
}).fail(function() {
    console.log('fail');
});

dfd.reject();   //Uncaught TypeError: dfd.reject is not a function, 因为promise对象没有reject属性

(三) state状态

function fn() {
    var dfd = $.Deferred();

    console.log(dfd.state());       //pending

    setTimeout(function() {
        dfd.resolve();
        console.log(dfd.state());   //resolved
    },1000);

    return dfd.promise();
}

var dfd = fn();

dfd.done(function() {
    console.log('success');         //success
}).fail(function() {
    console.log('fail');
});

(四) always

function fn() {
    var dfd = $.Deferred();

    //dfd.resolve();
    dfd.reject();  //不管是什么状态,always都会触发

    return dfd.promise();
}

var dfd = fn();

dfd.always(function() {
    console.log('111');
})

(五) then

function fn() {
var dfd = $.Deferred();
    //dfd.resolve();  //success
    //dfd.reject();   //fail
    dfd.notify('hi');     //progress
    return dfd.promise();
}

var dfd = fn();

dfd.then(
	function() {
	    alert('success');
	},
	function() {
	    alert('fail');
	},
	function() {
	    alert('progress');
	    alert(arguments[0]);    //hi
	}
);

then的函数如果有返回值

function fn1() {
    var dfd = $.Deferred();

    dfd.resolve();

    return dfd;
}


var dfd = fn1();

dfd = dfd.then(function(){
	 return 'then return value'; //如果返回值不是deffered或promise对象,则在源代码内部直接fireWith,会触发下面的done函数
});

dfd.done(function() {
	console.log(arguments[0]); //then return value
});

then/pipe(管道)的函数如果返回值是deffered对象

function fn1() {
    var dfd = $.Deferred();

    dfd.resolve();

    return dfd;
}


var dfd = fn1();

dfd = dfd.then(function(){
    return dfd; //如果返回值是deffered对象
});

dfd.done(function() {
   console.log(arguments[0]);
});

(五) pipe

管道的意思,需要注意和then方法其实进行了合并,其实用的不是特别多

var dfd = $.Deferred();

	dfd.resolve('hi');

	//其实pipe和then是一样的,因此也是三个参数函数
	//分别对应resolve reject notify
	var newDfd = dfd.pipe(function() {
	    return arguments[0] + 'pass then';
	});

	newDfd.done(function() {
	    console.log(arguments[0])   //hipass then
})

(六) when

  • 所有的都是resolve才会done
  • 只要有一个rejectdone
function fn1() {
    var dfd = $.Deferred();

    dfd.resolve();

    return dfd;
}

function fn2() {
    var dfd = $.Deferred();

    dfd.resolve();
    return dfd;
}


$.when(fn1(),fn2()).done(function() {
    console.log("success"); //fn1和fn2都成功才会成功
})

8.2 $.when()

源码

jQuery.extend({
// Deferred helper
	when: function( subordinate /* , ..., subordinateN */ ) {
		var i = 0,
			//将arguments转化为数组
			resolveValues = core_slice.call( arguments ),
			length = resolveValues.length,

			// the count of uncompleted subordinates
			// 如果只有一个参数,会判断返回值是否是延迟对象,如果是则返回length = 1
			// 多参数remaining是length
			remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,

			// the master Deferred. If resolveValues consist of only a single Deferred, just use that.
			// 如果只有一个参数 remaining = 1,如果返回值是延迟对象,则deffered是when中的fn返回的延迟对象
			// 如果返回值不是deffered对象,则执行后面的$.Deffered
			//
			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
		// 如果没有参数需要执行 $.when().done()
		// 如果是一个参数且返回值是延迟对象,这里不执行
		// 如果是一个参数返回值不是延迟对象,这里也执行
		if ( !remaining ) {
			deferred.resolveWith( resolveContexts, resolveValues );
		}

		// 如果是一个参数fn,则返回的是这个参数的延迟对象对应的promise()
		return deferred.promise();
	}
});

内容解析

  • 所有的都是resolve才会done
  • 只要有一个rejectdone
function fn1() {
    var dfd = $.Deferred();
    dfd.resolve();
    return dfd;
}
function fn2() {
    var dfd = $.Deferred();
    dfd.reject();
    return dfd;
}
//when中的fn参数必须返回延迟对象
//如果不是返回延迟对象,则会跳过这个fn
//$.when().done(function)	会执行成功
//$.when(arg1,arg2).done() 可以传参数处理
//$.when(fn1(),'111').done(function) 仍然会执行成功
$.when(fn1(),fn2()).done(function() {
    console.log("success"); //fn1和fn2都成功才会成功
}).fail(function(){
	console.log("fail");    //fail
});

when传参情况

function fn1() {
    var dfd = $.Deferred();
    dfd.resolve();
    return dfd;
}

function fn2() {
    var dfd = $.Deferred();
    dfd.resolve();
    return dfd;
}


function fn() {
    var dfd = $.Deferred();
    dfd.resolve();
}

//1.无参情况
//无参数的情况下在when中新建了一个deffered对象
//并返回deffered.promise()
//在when内部触发了新建deffered对象的fireWith函数
//因此done对象可以执行
$.when().done(function() {
    console.log("success");
});

//2.只有一个参数,不返回延迟对象
//和第一种情况类似
$.when(fn()).done(function() {
    console.log("success");
});

//3.只有一个参数的情况,返回延迟对象
//没有在when中新建deffered对象,而是使用fn1传入的deffered对象进行了处理
//done函数也是和fn1返回的dfd对象对应
$.when(fn1()).done(function() {
    console.log("success");
});

//4.多个参数的情况
//使用计数器进行处理
$.when(fn1(),fn2()).done(function() {
    console.log("success");
});