一、由 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 ){ // 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