一、由 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之Deferred源码剖析知识点 #### 一、Deferred和Promise简介 jQuery的Deferred对象是jQuery在ES6的Promise概念出现之前的一种实现,它允许开发者将异步操作的结果通过链式调用的方式处理,避免了传统的...
2. Deferred源码分析 Deferred模块的框架可以分为两个部分:Deferred方法和when方法。Deferred方法是用于创建Deferred对象的,而when方法是用于管理多个Deferred对象的执行顺序的。 Deferred方法的结构可以分为三...
在JavaScript编程中,“Deferred”是一个重要的概念,尤其在异步编程和Promise机制中扮演着核心角色。 Deferred对象是jQuery引入的,后来被ES6的Promise所吸收,它为解决回调地狱问题提供了有效的解决方案。这篇博客...
- **异步队列Deferred**:探讨jQuery中用于处理异步操作的`Deferred`对象,解释其工作原理和如何实现链式调用。 - **队列Queue**:讲解队列在jQuery动画和AJAX请求中的作用,以及如何管理和控制队列中的任务执行...
4. **延迟渲染(Deferred Shading)**:DOOM-3使用延迟渲染技术,先将所有几何信息存储到G-Buffer,然后在后处理阶段进行光照计算。这种方法允许进行复杂的光照效果,如法线贴图、高光反射等。 三、物理系统 DOOM-...
2. I/O子系统:I/O请求通过中断处理和DPC(Deferred Procedure Call)进行,确保实时性。 3. 驱动接口:VxWorks提供了标准的设备驱动接口,如open、close、read、write等,方便用户操作硬件设备。 4. 总线驱动:如...
- **Interrupt Service Routines (ISR)** 和 **DPCs (Deferred Procedure Calls)**:ISR处理设备中断,DPC则用于执行非时间敏感的中断后任务。 通过对这些源码的分析和实践,开发者不仅可以提升对Windows内核的理解...
例如,某些内部数据结构,如KPCR(Processor Control Region)和KDPC(Deferred Procedure Call Descriptor),揭示了系统如何管理和调度硬件资源。而未公开的函数则可能涉及到更高级别的系统操作,如安全机制、内存...
6. **性能优化**:jQuery在设计时考虑了性能问题,如使用Sizzle选择器引擎优化DOM查找,使用缓存避免重复操作,以及延迟执行(`.deferred()`)和节流/debounce(防止频繁触发)等技术。 7. **源码结构分析**:jQuery...
在深入讨论jQuery源码中Callbacks模块的内部实现之前,先要明确什么是Callbacks以及它在JavaScript编程中的作用。Callbacks,即回调函数,在JavaScript中扮演着至关重要的角色。由于JavaScript是基于单线程事件循环...
### jQuery源码分析关键知识点详解 #### 一、前言 在深入了解jQuery源码之前,有必要先简要介绍一下jQuery的基本情况及其对JavaScript编程领域的重要意义。jQuery作为一个轻量级、功能丰富的JavaScript库,在Web...
- `readyList`: 用于处理DOM ready事件的递延对象(deferred object)。 - `document`: 当前窗口的文档对象模型(DOM)。 - `location`: 当前窗口的位置信息。 - `navigator`: 浏览器的信息,如名称、版本等。 - `_...
jQuery源码分析 00 前言开光 01 总体架构 03 构造jQuery对象-源码结构和核心函数 ...05 异步队列 Deferred 08 队列 Queue 09 属性操作 10 事件处理-Event-概述和基础知识 15 AJAX-前置过滤器和请求分发器
GWT提供了多种优化策略,包括代码分割(Code Splitting)以减少初始加载时间,延迟加载(Deferred Binding)根据浏览器特性生成定制的JavaScript,以及压缩和合并JavaScript文件以减小下载体积。 9. **社区与生态*...
jQuery.Deferred()用来构造一个Deferred对象。该对象有状态值,共有三种: Rejected, Resolved和初始状态。其中Resolved表示该操作成功完成了,而Rejected 则表示出现了错误,调用失败。Deferred对象的主要成员如下:...
9. **中断处理**:中断服务例程(ISR)是响应硬件中断的函数,用于快速处理中断事件,然后通过DPC(Deferred Procedure Call)或线程上下文完成剩余工作。 10. **调试工具**:如WinDbg,是驱动开发中常用的调试工具...
9. **延迟对象(Deferred)**:jQuery引入了 Deferred 对象来处理异步操作,使得回调函数的编写更加清晰和灵活。 10. **性能优化**:jQuery源码中包含了许多性能优化技巧,如缓存查找结果、批量操作和使用`...