概述
参考文章:http://schifred.iteye.com/blog/2317239
一、由 function(args,callback){} 到 Deferred.resolve().done(fn)
主要问题:添加多个回调函数需要在function函数体内进行改写,针对resovle函数执行成功、reject函数执行失败、progress函数执行过程中三种状态分别注册回调函数较为麻烦,以及对回调函数的处理机制没有通用性,可移植性差。
功能需要:降解回调函数注册和执行的复杂度,针对失败、成功、执行中三种状态能批量添加回调函数,处理回调函数的通用功能抽象为独立的模块。
设计思路:利用Event模块事件触发、绑定事件的理念,将原回调函数执行过程callback()改写为Event.emit(eventName),而具体的回调函数通过Event.addListener(eventName,fns)注册加载,这样既能实现根据执行状态的不同,分别调用特定的回调函数,同时也能解决批量注册的问题。
具体实现:
1.Callbacks对象的fire、fireWith方法同Event模块的emit方法,而add方法等同Event模块的addListener方法。针对resolve、reject、progress三种状态分别注册回调函数队列问题,可以通过构建三个Callbacks对象解决。
2.Deferred对象的tuples变量以数组形式存储resovle、reject、progress三种状态的fire方法名、add方法名、Callbacks对象、状态值(resovled|rejected),使三个Callbacks对象发生作用。
var tuples = [
[ "notify", "progress", jQuery.Callbacks( "memory" ),
jQuery.Callbacks( "memory" ), 2 ],
[ "resolve", "done", jQuery.Callbacks( "once memory" ),
jQuery.Callbacks( "once memory" ), 0, "resolved" ],
[ "reject", "fail", jQuery.Callbacks( "once memory" ),
jQuery.Callbacks( "once memory" ), 1, "rejected" ]
]
3.回调函数队列Callbacks的add方法,通过deferred[ tuples[i][1] ]=tuples[i][2].add挂载给Deferred对象;同时函数队列Callbacks的fire|fireWith方法,通过promise[ tuples[i][0] | tuples[i][0]+”With” ]=tuples[i][2].fire|fireWith挂载给promise对象。
deferred.progress注册执行过程中回调函数队列,状态为pending,promise.notify方法为触发函数。
deferred.done注册执行完成时回调函数队列,状态为resovled,promise.resovle方法为触发函数。
deferred.fail注册执行失败时回调函数队列,状态为rejected,promise.reject方法为触发函数。
当deferred.resolve触发时,执行状态改为resolved,也即执行成功,使rejected状态下触发的回调函数队列失效,并且锁死pending状态下触发的回调函数队列。deferred.reject触发时类同。
jQuery.each( tuples, function( i, tuple ) {
var list = tuple[ 2 ],
stateString = tuple[ 5 ];
promise[ tuple[ 1 ] ] = list.add;
if ( stateString ) {
list.add(
function() {
state = stateString;
},
tuples[ 3 - i ][ 2 ].disable,
tuples[ 0 ][ 2 ].lock
);
}
list.add( tuple[ 3 ].fire );
deferred[ tuple[ 0 ] ] = function() {
deferred[ tuple[ 0 ] + "With" ]( this === deferred ? undefined : this, arguments );
return this;
};
deferred[ tuple[ 0 ] + "With" ] = list.fireWith;
} );
4.与此同时,在deferred对象的基础上构造promise对象,为的是改变函数执行状态的fire方法不能在函数外部被调用,函数外部只能使用add方法添加回调函数,以使外部的一切处理不致影响运行逻辑。
var dtd=$.Deferred();
function wait(dtd){
setTimeout(function(){
dtd.resovle();
},3000)
// 返回promise对象,函数外不能改变Deferred对象状态
return dtd.promise()
}
wait(dtd).done(function(){
console.log("I'm back!")
})
二、由Deferred.resolve().done(fn)到Deferred.pipe(fnDone,fnFail,fnProgress)
主要问题:添加三种状态的回调函数时需要分别使用progress|done|fail方法;只能处理单个阻塞函数,不能处理阻塞函数的递延,即阻塞函数A执行完成后,再执行阻塞函数B;回调函数队列使用参数由触发函数resovle、reject、notify提供,不能进行过滤处理,即由A回调处理后再传给B回调。
功能需要:实现pending、resovled、rejected状态回调函数队列的一次性注册;多个阻塞函数的递延执行;回调函数的参数在调用过程实现动态改变,并传给下一个回调函数。
设计思路和具体实现:
1.三种状态的回调函数fnDone、fnFail、fnProgress分别加载在原deferred对象的done、fail、progress函数队列。
2.链式调用的考虑:递归创建新的deferred对象newDefer,返回newDefer.promise(),newDefer的触发函数resovle|reject|notify添加到原deferred对象对应回调函数队列中,以使newDefer后续注册的done、progress、fire函数队列能触发执行,即利用事件机制将newDefer函数队列的启动器emit方法加入原deferred对象的回调队列中。
3.若fnDone、fnFail、fnProgress返回值是普通函数,将fnDone、fnFail、fnProgress添加到原deferred对象对应回调函数队列之外,再将newDefer的触发函数resovle|reject|notify添加到该队列中,即可保证原deferred对象的状态能相应触发fnDone、fnFail、fnProgress函数得到执行,以及newDefer对象注册的函数队列同样得到执行。
同时fnDone、fnFail、fnProgress函数将def.resovle|reject|notify(args)所传参数过滤处理以后,传给通过newDefer注册的回调函数队列。
4.回调函数fnDone、fnFail、fnProgress的返回值是deferred对象def:在此种情况下,回调函数fnDone、fnFail、fnProgress内部预先设置了def状态改变的触发时机,即内部调用def.resovle|notify|reject,通过newDefer.promise()对象后续注册的函数队列执行时机是在def状态改变的时候,也即需要添加到def对象相应的回调函数队列中。
5.图解
6.源码
pipe: function( /* fnDone, fnFail, fnProgress */ ) {
var fns = arguments;
return jQuery.Deferred( function( newDefer ) {
jQuery.each( tuples, function( i, tuple ) {
// Map tuples (progress, done, fail) to arguments (done, fail, progress)
var fn = jQuery.isFunction( fns[ tuple[ 4 ] ] ) && fns[ tuple[ 4 ] ];
// deferred.progress(function() { bind to newDefer or newDefer.notify })
// deferred.done(function() { bind to newDefer or newDefer.resolve })
// deferred.fail(function() { bind to newDefer or newDefer.reject })
deferred[ tuple[ 1 ] ]( function() {
var returned = fn && fn.apply( this, arguments );
if ( returned && jQuery.isFunction( returned.promise ) ) {
returned.promise()
.progress( newDefer.notify )
.done( newDefer.resolve )
.fail( newDefer.reject );
} else {
newDefer[ tuple[ 0 ] + "With" ](
this,
fn ? [ returned ] : arguments
);
}
} );
} );
fns = null;
} ).promise();
}
7.多个阻塞函数的问题:通过构建新的deferred对象newDefer,不仅解决了链式调用的问题,与此同时,通过newDefer注册的回调函数队列在延迟对象def内部函数def.resovle改变状态值时触发执行,因此能满足一个阻塞函数执行完毕后再调用另一个阻塞函数的需求。
var dtd=$.Deferred();
dtd.resovle()
.pipe(function(){
return $.ajax("getJson.json")
}).pipe(function(){
return $.ajax("getJson.json")
}).pipe(function(){
console.log("All ajax methods are over.")
})
8.参数过滤问题(针对fnDone、fnFail、fnProgress的返回值是普通函数的情况):fnDone、fnFail、fnProgress在原deferred对象的回调函数队列中执行,执行完毕后调用延迟对象newDefer的resolveWith、rejectWith、notifyWith方法,触发newDefer的回调函数队列执行,两者在同一函数中,因此fnDone、fnFail、fnProgress的返回值可以作为参数传入newDefer的resolveWith、rejectWith、notifyWith方法,再由newDefer的回调函数队列获得。
var dtd=$.Deferred();
dtd.resovle()
.pipe(function(){
return 5
}).done(function(x){
console.log(x*2) // 10
})
三、由Deferred.pipe(fnDone,fnFail,fnProgress) 到 Deferred.then(onFulfilled,onRejected,onProgress)
主要问题:延迟函数若有如IO获取数据失败等错误发生时,将直接报错,后续函数没法得到执行。
功能需求:捕获错误并作处理。
**设计思路:**reject状态的预执行函数调用throw函数抛出错误,接着调用try-catch函数捕获该错误,完成错误捕捉以后,再执行rejectWith方法,以使通过fail方法注册的失败装填函数队列得到执行。
具体实现:
1.通过Deferred.then方法注册的回调函数添加到另外三个Callbacks对象tuples[i][3]中,原Deferred对象通过done、fail方法预先注册的回调函数在tuples[i][2]中,两者不相干扰。
问题:Deferred对象对外没有提供访问、处理相关Callbacks对象tuples[i][2]、tuples[i][3]的接口,使得Deferred对象类同Dom元素的事件机制,对外只提供注册事件和触发事件的方法,而不能对注册的事件进行删除、改写处理,这是我的学识浅薄,还是原作者尚未考虑。
then: function( onFulfilled, onRejected, onProgress ) {
var maxDepth = 0;
// do something
return jQuery.Deferred( function( newDefer ) {
// progress_handlers.add( ... )
tuples[ 0 ][ 3 ].add(
resolve(
0,
newDefer,
jQuery.isFunction( onProgress ) ?
onProgress :
Identity,
newDefer.notifyWith
)
);
// fulfilled_handlers.add( ... )
tuples[ 1 ][ 3 ].add(
resolve(
0,
newDefer,
jQuery.isFunction( onFulfilled ) ?
onFulfilled :
Identity
)
);
// rejected_handlers.add( ... )
tuples[ 2 ][ 3 ].add(
resolve(
0,
newDefer,
jQuery.isFunction( onRejected ) ?
onRejected :
Thrower
)
);
} ).promise();
},
2.构建resovle函数,区分progress、reject、resovle三种状态,以及参数项返回值是否为deferred延迟对象两种情况。
2-1.参数项函数的返回值为deferred延迟对象,逻辑上的需要便是原Deferred对象延迟执行完成后,通过Deferred.then方法的返回值newDefer.done|fail|progress方法注册的函数队列得到执行,实现方案只要将启动函数newDefer.notifyWith|resovleWith|rejectWith加入相应的tuples[i][3]队列。
2-2.参数项函数的返回值为deferred延迟对象,逻辑上的需要是在该deferred延迟对象执行完成后,也即deferred.resovle|reject|notify方法执行时,使通过Deferred.then的返回值newDefer.done|fail|progress方法注册的回调函数队列得到执行,即把newDefer.resovleWith|rejectWith|notifyWith添加到deferred延迟对象的tuples[i][3]回调函数队列。Deferred.then方法中奇异地使用三次嵌套resovle函数实现,这让人匪夷所思,其中的疑问包括,调用deferred.then方法创建的anotherDefer对象,使用者并不能调用该对象的done|fail|progress方法注册函数,因此存在多余的感觉。
图解
源码
then: function( onFulfilled, onRejected, onProgress ) {
var maxDepth = 0;
function resolve( depth, deferred, handler, special ) {
return function() {
var that = this,
args = arguments,
mightThrow = function() {
var returned, then;
// Support: Promises/A+ section 2.3.3.3.3
// https://promisesaplus.com/#point-59
// Ignore double-resolution attempts
if ( depth < maxDepth ) {
return;
}
returned = handler.apply( that, args );
// Support: Promises/A+ section 2.3.1
// https://promisesaplus.com/#point-48
if ( returned === deferred.promise() ) {
throw new TypeError( "Thenable self-resolution" );
}
// Support: Promises/A+ sections 2.3.3.1, 3.5
// https://promisesaplus.com/#point-54
// https://promisesaplus.com/#point-75
// Retrieve `then` only once
then = returned &&
// Support: Promises/A+ section 2.3.4
// https://promisesaplus.com/#point-64
// Only check objects and functions for thenability
( typeof returned === "object" ||
typeof returned === "function" ) &&
returned.then;
// Handle a returned thenable
if ( jQuery.isFunction( then ) ) {
// Special processors (notify) just wait for resolution
if ( special ) {
then.call(
returned,
resolve( maxDepth, deferred, Identity, special ),
resolve( maxDepth, deferred, Thrower, special )
);
// Normal processors (resolve) also hook into progress
} else {
// ...and disregard older resolution values
maxDepth++;
then.call(
returned,
resolve( maxDepth, deferred, Identity, special ),
resolve( maxDepth, deferred, Thrower, special ),
resolve( maxDepth, deferred, Identity,
deferred.notifyWith )
);
}
// Handle all other returned values
} else {
// Only substitute handlers pass on context
// and multiple values (non-spec behavior)
if ( handler !== Identity ) {
that = undefined;
args = [ returned ];
}
// Process the value(s)
// Default process is resolve
( special || deferred.resolveWith )( that, args );
}
},
// Only normal processors (resolve) catch and reject exceptions
process = special ?
mightThrow :
function() {
try {
mightThrow();
} catch ( e ) {
if ( jQuery.Deferred.exceptionHook ) {
jQuery.Deferred.exceptionHook( e,
process.stackTrace );
}
// Support: Promises/A+ section 2.3.3.3.4.1
// https://promisesaplus.com/#point-61
// Ignore post-resolution exceptions
if ( depth + 1 >= maxDepth ) {
// Only substitute handlers pass on context
// and multiple values (non-spec behavior)
if ( handler !== Thrower ) {
that = undefined;
args = [ e ];
}
deferred.rejectWith( that, args );
}
}
};
// Support: Promises/A+ section 2.3.3.3.1
// https://promisesaplus.com/#point-57
// Re-resolve promises immediately to dodge false rejection from
// subsequent errors
if ( depth ) {
process();
} else {
// Call an optional hook to record the stack, in case of exception
// since it's otherwise lost when execution goes async
if ( jQuery.Deferred.getStackHook ) {
process.stackTrace = jQuery.Deferred.getStackHook();
}
window.setTimeout( process );
}
};
}
return jQuery.Deferred( function( newDefer ) {
// progress_handlers.add( ... )
tuples[ 0 ][ 3 ].add(
resolve(
0,
newDefer,
jQuery.isFunction( onProgress ) ?
onProgress :
Identity,
newDefer.notifyWith
)
);
// fulfilled_handlers.add( ... )
tuples[ 1 ][ 3 ].add(
resolve(
0,
newDefer,
jQuery.isFunction( onFulfilled ) ?
onFulfilled :
Identity
)
);
// rejected_handlers.add( ... )
tuples[ 2 ][ 3 ].add(
resolve(
0,
newDefer,
jQuery.isFunction( onRejected ) ?
onRejected :
Thrower
)
);
} ).promise();
}
四、由Deferred.resolve().done(fn)到$.when(deferred).done(fn)
主要问题: Deferred.resolve().done(fn)只能根据一个延迟对象执行完毕后再调用回调函数fn,当需要根据多个延迟对象最末一个执行完成后调用回调函数fn时,Deferred.resolve().done(fn)就鞭长莫及了,$.when(deferred).done(fn)用于解决这一问题。
功能需求:多个延迟对象最末一个执行完毕后调用回调函数。
设计思路:使用闭包变量remaining记录延迟对象的个数,当其中一个执行完成后,remaining-1,因此最末一个执行完成后,remaining为0,此时便可触发后续注册的回调函数得到执行。
具体实现:创建一个新的Deferred对象master,且master.promise作为$.when
的返回值,因此在$.when
方法内部,可使用master.resovleWith启动以master.done注册的回调函数得到执行,其中master.resovleWith方法中this关键字指向最后一个执行完毕的deferred对象,参数也由该deferred对象的resovle方法提供。
源码:
function adoptValue( value, resolve, reject ) {
var method;
try {
// Check for promise aspect first to privilege synchronous behavior
if ( value && jQuery.isFunction( ( method = value.promise ) ) ) {
method.call( value ).done( resolve ).fail( reject );
// Other thenables
} else if ( value && jQuery.isFunction( ( method = value.then ) ) ) {
method.call( value, resolve, reject );
// Other non-thenables
} else {
// Support: Android 4.0 only
// Strict mode functions invoked without .call/.apply get global-object context
resolve.call( undefined, value );
}
// For Promises/A+, convert exceptions into rejections
// Since jQuery.when doesn't unwrap thenables, we can skip the extra checks appearing in
// Deferred#then to conditionally suppress rejection.
} catch ( value ) {
// Support: Android 4.0 only
// Strict mode functions invoked without .call/.apply get global-object context
reject.call( undefined, value );
}
}
jQuery.extend( {
when: function( singleValue ) {
var
// count of uncompleted subordinates
remaining = arguments.length,
// count of unprocessed arguments
i = remaining,
// subordinate fulfillment data
resolveContexts = Array( i ),
resolveValues = slice.call( arguments ),
// the master Deferred
master = jQuery.Deferred(),
// subordinate callback factory
updateFunc = function( i ) {
return function( value ) {
resolveContexts[ i ] = this;
resolveValues[ i ] = arguments.length > 1 ? slice.call( arguments ) : value;
if ( !( --remaining ) ) {
master.resolveWith( resolveContexts, resolveValues );
}
};
};
// Single- and empty arguments are adopted like Promise.resolve
if ( remaining <= 1 ) {
adoptValue( singleValue, master.done( updateFunc( i ) ).resolve, master.reject );
// Use .then() to unwrap secondary thenables (cf. gh-3000)
if ( master.state() === "pending" ||
jQuery.isFunction( resolveValues[ i ] && resolveValues[ i ].then ) ) {
return master.then();
}
}
// Multiple arguments are aggregated like Promise.all array elements
while ( i-- ) {
adoptValue( resolveValues[ i ], updateFunc( i ), master.reject );
}
return master.promise();
}
} );
使用:
$.when(
$.ajax("getJson1.json"),
$.ajax("getJson2.json")// 假使延迟最久
).done(
function(res){
console.log(res)// 由getJson2.json的返回值构成
}
)
五、整体源码解读(初稿)
define([
"./core",
"./var/slice",
"./callbacks"// 回调函数的队列形式调用实现
], function(jQuery,slice){
function Identity(v){
return v;
}
function Thrower(ex){
throw ex;
}
jQuery.extend({
Deferred:function(func){
var tuples=[
// action, add listener, callbacks,
// ... .then handlers, argument index, [final state]
["notify","progress",jQuery.Callbacks("memory"),jQuery.Callbacks("memory"),2],
["resolve","done",jQuery.Callbacks("once memory"),jQuery.Callbacks("once memory"),0, "resolved"],
["reject","fail",jQuery.Callbacks("once memory"),jQuery.Callbacks("once memory"),1, "rejected"]
],
state="pending",
promise={
state:function(){
return state;// "pending"、"resolved"、"rejected"三种状态,返回状态值
},
always:function(){// 成功或失败时均调用arguments函数
deferred.done(arguments).fail(arguments);
return this;
},
"catch":function(fn){
return promise.then(null,fn);
},
// Keep pipe for back-compat
// 若参数为deferred对象:
// 注册一个新的deferred对象,起始deferred对象调用notify、resolve、reject方法时触发新deferred对象的同类方法
// 进而执行新deferred对象中progress、done、fail方法注册的函数
// 示例:
// var dtd=$.Deferred(),deferred=$.Deferred();
// function wait(dad){
// setTimeout(function(){
// dtd.resovle();
// },3000);
//
// return dtd.promise();
// }
// wait(dtd).pipe($.Deferred())// 获得$.Deferred()返回对象
// .done(fn);
// 实现功能似,$.Deferred()返回对象done方法中添加fn函数,dtd的done方法中也添加fn函数
// dtd.resovle方法控制着dtd、$.Deferred()返回对象的done方法执行
// 若参数为普通函数:
// 以deferred.resovle方法传入的参数作为函数的参数,直接执行该函数,函数的返回值作为done、fail方法的参数
// 或者将deferred.resovle方法传入的参数作为done、fail方法的参数
// 示例:
// var dtd=$.Deferred();
// function wait(dad){
// setTimeout(function(){
// dtd.resovle(a,b,c);
// },3000);
//
// return dtd.promise();
// }
// wait(dtd).pipe(fn1)
// .done(fn2)
// fn1用来格式化dtd.resovle(a,b,c)中a、b、c参数,作为参数传给fn2
// 若fn1不存在,直接传递a、b、c给fn2
//
// done,fail,progress状态执行函数顺序排列,tuples数组元素第四项为对应函数在参数arguments中的位置
pipe:function(/* fnDone, fnFail, fnProgress */){
var fns=arguments;
return jQuery.Deferred( function(newDefer){// newDefer即使用jQuery.Deferred新创建的deferred对象
jQuery.each(tuples, function(i,tuple){
// fns[tuple[4]]获取pipe方法中done,fail,progress状态执行函数,即pipe方法的参数
var fn=jQuery.isFunction(fns[tuple[4]]) && fns[tuple[4]];
// 执行deferred.progress|done|fail方法
deferred[tuple[1]](function(){
var returned=fn && fn.apply(this,arguments);
if ( returned && jQuery.isFunction(returned.promise) ){
returned.promise()
// 对于pipe方法返回值done|fail注册的函数,挂载启动函数
.progress(newDefer.notify)
.done(newDefer.resolve)
.fail(newDefer.reject);
}else{
// 对于pipe方法返回值done|fail注册的函数,挂载启动函数
newDefer[tuple[0]+"With"](
this===promise ? newDefer.promise() : this,
fn ? [returned] : arguments
);
}
});
});
fns=null;
// 返回newDefer的promise对象,使其不能使用deferred对象的resolve、reject方法在外部改变状态
// 链式使用done|fail方法注册函数时,启动函数在pipe方法执行中已挂载
}).promise();
},
then:function(onFulfilled,onRejected,onProgress){
var maxDepth=0;
function resolve(depth, deferred, handler, special){
// 返回函数注册给eferred对象的tuples[i][3]
// 通过deferred.notify|resolve|reject方法启动执行,因此是在延迟函数执行过程或完毕以后调用
return function(){
var that= this===promise ? undefined : this,
// arguments由tuples[i][2]中fire方法传入
// 即deferred对象的progress、done、fail方法传入
args= arguments,
mightThrow=function(){
var returned,then;
// Support: Promises/A+ section 2.3.3.3.3
// https://promisesaplus.com/#point-59
// Ignore double-resolution attempts
if ( depth<maxDepth ){
return;
}
// 执行onFulfilled、onRejected、onProgress函数,在三项有且为函数的前提下
// 否则执行Identity函数返回args,或执行Thrower方法报错(针对rejected状态)
returned=handler.apply(that,args);
// Support: Promises/A+ section 2.3.1
// https://promisesaplus.com/#point-48
if ( returned===deferred.promise() ){
throw new TypeError("Thenable self-resolution");
}
// promise.then方法所传参数deferred对象或其构造函数
then=returned &&
( typeof returned==="object" || typeof returned==="function" ) &&
returned.then;
// Handle a returned thenable
if ( jQuery.isFunction(then) ){
// Special processors (notify) just wait for resolution
if ( special ){
// promise.then方法所传参数deferred对象或其构造函数时
// returned指向该deferred对象
// 原deferred对象tuples[i][3]注册函数队列
//
// 递归调用then方法再次创建deferred对象
// 该deferred对象then方法在原deferred.done函数队列中添加
// 即原deferred函数队列执行完毕后触发参数deferred对象的函数队列注册
// 再次创建deferred对象函数队列中添加参数deferred对象的启动函数
then.call(
returned,
// then方法创建的anotherDefer回调队列中注册newDefer.notifyWidth
resolve(maxDepth,deferred,Identity,special),
resolve(maxDepth,deferred,Thrower,special)
);
// Normal processors (resolve) also hook into progress
}else{
// ...and disregard older resolution values
maxDepth++;
then.call(
returned,
resolve(maxDepth,deferred,Identity,special),
resolve(maxDepth,deferred,Thrower,special),
resolve(maxDepth,deferred,Identity,deferred.notify)
);
}
// Handle all other returned values
}else{
// Only substitute handlers pass on context
// and multiple values (non-spec behavior)
if ( handler!==Identity ){
that=undefined;
args=[ returned ];
}
// 调用新创建的deferred对象notifyWith、resolveWith方法
// 该deferred对象的promise又是then方法最终返回值,用于递归应用阻塞回调
// 示例:
// var promise1=$.ajax(url1);
// var promise2=promise1.then(function(data){
// return $.ajax(url2,{data:data});
// });
// var promise3=promise2.then(function(data){
// return $.ajax(url3,{data:data});
// });
// promise3.done(function(data){
// // data retrieved from url3
// });
//
// tuple[i][3]函数队列中添加then方法新建的deferred对象函数队列的启动函数
( special||deferred.resolveWith )( that||deferred.promise(),args );
}
},
// Only normal processors (resolve) catch and reject exceptions
process=special ?
mightThrow :
function(){
try{
mightThrow();
}catch(e){
if ( jQuery.Deferred.exceptionHook ){
jQuery.Deferred.exceptionHook(e,process.stackTrace);
}
// Support: Promises/A+ section 2.3.3.3.4.1
// https://promisesaplus.com/#point-61
// Ignore post-resolution exceptions
if ( depth+1>=maxDepth ){
// Only substitute handlers pass on context
// and multiple values (non-spec behavior)
if ( handler!==Thrower ){
that=undefined;
args=[e];
}
deferred.rejectWith(that||deferred.promise(),args);
}
}
};
// Support: Promises/A+ section 2.3.3.3.1
// https://promisesaplus.com/#point-57
// Re-resolve promises immediately to dodge false rejection from
// subsequent errors
if ( depth ){
process();
}else{
// 获取堆栈信息???
if ( jQuery.Deferred.getStackHook ){
process.stackTrace=jQuery.Deferred.getStackHook();
}
window.setTimeout(process);// 不是立即执行???
}
};
}
return jQuery.Deferred(function(newDefer){
// deferred对象的tuples[i][3]添加resovle函数返回的函数队列
// 通过deferred.notify|resolve|reject方法启动执行,因此是在延迟函数执行过程或完毕以后调用
tuples[0][3].add(
resolve(
0,
newDefer,
jQuery.isFunction(onProgress) ? onProgress : Identity,
newDefer.notifyWith
)
);
tuples[1][3].add(
resolve(
0,
newDefer,
jQuery.isFunction( onFulfilled ) ? onFulfilled : Identity
)
);
tuples[2][3].add(
resolve(
0,
newDefer,
jQuery.isFunction( onRejected ) ? onRejected : Thrower
)
);
}).promise();
// 创建新的deferred对象,链式注册done|fail函数队列
// 该函数队列的触发函数加载到原有deferred对象的tuple[i][3]中
},
// 将promise拷贝给obj,或者返回promise,deferred模块中应用是拷贝给deferred
promise: function(obj){
return obj!=null?jQuery.extend(obj,promise):promise;
}
},
deferred={};
// 以Callbacks构造deferred对象progress、done、fail方法的函数存储域
// promise.progress|done|fail执行该三组队列函数的注册
// deferred.notify|resolve|rejeact|deferred.notifyWith|resolveWith|rejeactWith
// 即时执行相应的函数队列,并执行tuple[3]中的函数队列(promise.then方法注册)
// done函数队列率先引入改变state状态值,使fail函数队列失效,progress队列锁定的函数,fail函数相应处理
// deferred在延时函数内部使用,延时函数返回promise对象,实现外部接口只能注册add、不能启动fire
jQuery.each(tuples, function(i,tuple){
var list=tuple[2],// Callbacks对象实例,提供done,fail,progress状态执行函数的添加和触发
stateString=tuple[5];// progress状态为空,done状态为resolved,fail状态为rejected
// promise.progress、promise.done、promise.fail方法添加各自状态的回调函数
// list指向Callbacks对象,以此驻留在内存中
promise[tuple[1]]=list.add;
// 向done和fail函数队列添加改变状态值state的函数,state分为pending、resolved、rejected三种
// 同时done函数队列执行时使fail函数队列失效,progress函数队列锁定
// fail函数队列执行时使done函数失效,progress函数队列锁定
if ( stateString ){
list.add(
function(){
state=stateString;
},
tuples[3-i][2].disable,// done、fail函数队列交互失效
tuples[0][2].lock// progress函数队列锁定
);
}
// promise.then方法将函数队列注册在相应的tuple[3]的Callbacks对象里
// tuple[2]中添加tuple[3].fire,以执行tuple[3]中注册的函数队列
list.add(tuple[3].fire);
// deferred.notify触发执行progress状态回调函数,也即promise.progress方法注册的函数
// deferred.resolve触发执行done状态回调函数,也即promise.done方法注册的函数
// deferred.reject触发执行fail状态回调函数,也即promise.fail方法注册的函数
deferred[tuple[0]]=function(){
deferred[tuple[0]+"With"](this===deferred?promise:this, arguments);
return this;
};
// deferred.notifyWith触发执行progress状态回调函数
// deferred.resolveWith触发执行done状态回调函数
// deferred.rejectWith触发执行fail状态回调函数
deferred[tuple[0]+"With"]=list.fireWith;
} );
// 将promise方法拷贝给deferred
promise.promise(deferred);
// Deferred函数执行完成后回调函数,func用以改造deferred对象
if (func){
func.call(deferred,deferred);
}
return deferred;
},
// var dtd=$.Deferred();
// function wait(dtd){
// setTimeout(function(){
// dtd.resovle()
// },3000)
//
// return dtd.promise()
// }
// $.when(wait(dtd)).done(function(){console.log("success")})
//
// $.when方法主要意图:多个延迟对象同时执行,最末一个执行完毕,调用done方法注册的函数
// 实际境况:触发函数在$.when(dtd)参数dtd函数中
// 函数队列在$.when(dtd).done(fn).fail(fn)中以done|fail方法注册
// 主要问题:dtd.resovle参数传入函数队列
// $.when参数有多个延迟对象[dtd]时,判断dtd执行完毕总时间
// 实现方案:以dtd.resovle方法启动dtd.done()注册的函数队列
// 构造新的deferred对象master,且master.promise()作为返回结果
// master.resovle|reject方法加入dtd.done()函数队列,因此dtd.resovle方法同时启动master的函数队列
// 同时参数由dtd.resovle传入dtd.done(),最终传入master.done|fail方法中
// 多个延迟对象通过闭包外参数remaining判断这几个延迟对象是否执行完毕
// 设计:updateFunc方法在$.when()参数延迟对象执行完毕后调用
// 用remaining判断$.when()参数延迟对象是否执行完毕
// remaining==0时触发执行$.when().done()注册的函数队列
// 通过条件语句区分$.when()参数为deferred对象,还是普通函数,或者为空
// 若为延迟对象,通过deferred.done|then注册updateFunc,执行$.when().done()的函数队列
// 若为普通函数,立即调用updateFunc,执行$.when().done()的函数队列,传参为$.when()中参数
// 若为空,立即执行$.when().done()的函数队列
when:function(){
var method,resolveContexts,
i=0,
resolveValues=slice.call(arguments),
length=resolveValues.length,
remaining=length,
master=jQuery.Deferred(),// 构造新的deferred对象,$.when各参数deferred对象执行完毕后执行该对象的done|fail方法
// 通过闭包驻留remaining,用以判断$.when各参数deferred对象均执行完毕
// 执行完毕通过master.resolveWith方法,执行$.when().done()中done方法注册的函数
updateFunc=function(i){
return function(value){// value为$.when各参数deferred对象的resolve|resovleWith方法带给done方法的参数
resolveContexts[i]=this;
resolveValues[i]=arguments.length>1 ? slice.call(arguments) : value;
if ( !(--remaining) ) {
master.resolveWith(
resolveContexts.length===1 ? resolveContexts[0] : resolveContexts,
resolveValues
);
}
};
};
if ( length>0 ){
resolveContexts=new Array(length);// 数组形式存储$.when各参数deferred对象
for ( ; i<length; i++ ){
if ( resolveValues[i] && jQuery.isFunction(( method=resolveValues[i].promise )) ){
// 以done方法将master.resovleWith添入延迟对象dtd.done()函数队列中
method.call( resolveValues[i] )
// $.when各参数deferred对象执行promise方法的时候,this关键字指向该deferred对象
.done( updateFunc(i) )
// $.when各参数deferred对象均执行成功时,即调用$.when().done()
// 参数为各参数deferred对象resolve|resovleWith方法添加的参数中done方法注册的函数
.fail( master.reject );
// $.when各参数deferred对象有一个执行失败,即调用$.when().fail()中fail方法注册的函数
}else if( resolveValues[i] && jQuery.isFunction(( method=resolveValues[i].then )) ){
// 以then方法将master.resovleWith添入延迟对象dtd.done()函数队列中
method.call( resolveValues[i], updateFunc(i), master.reject);
}else{
// 如果参数不是deferred对象,立即调用$.when().done()中done方法注册的函数
// done方法传入的参数由$.when参数构成
updateFunc(i)( resolveValues[i] );
}
}
}else{
// 没有参数,立即调用$.when().done()中done方法注册的函数
master.resolveWith();
}
return master.promise();
}
});
return jQuery;
});
最后
以上就是独特向日葵为你收集整理的jquery $.deferredd对象的全部内容,希望文章能够帮你解决jquery $.deferredd对象所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复