jquery $.deferredd对象

参考文章: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;  
});  
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值