jQuery回调、递延对象总结(中篇) —— 神奇的then方法

前言:

什么叫做递延对象,生成一个递延对象只需调用jQuery.Deferred函数,deferred这个单词译为延期,推迟,即延迟的意思,那么在jQuery中

又是如何表达延迟的呢,从递延对象中的then方法或许能找到这种延迟的行为,本文重点解读递延对象中的then方法

 

jQuery回调、递延对象总结篇索引:

jQuery回调、递延对象总结(上篇)—— jQuery.Callbacks

jQuery回调、递延对象总结(中篇) —— 神奇的then方法

jQuery回调、递延对象总结(下篇) —— 解密jQuery.when方法

 

设计思路:

在递延对象构造中,分别有三组回调对象,每一组回调对象都有与之对应的行为(action,add listener),和状态(final state),

这些行为都归纳为递延对象中的(触发回调,添加函数到回调列表中等)方法

 

jQuery.Deferred构造源码

Deferred构造源码除了then函数源码外,其他都非常简单,这里不做过多解读,后面将重点讨论then方法

jQuery.extend({



    Deferred: function( func ) {

        var tuples = [

                // action, add listener, listener list, final state

                [ "resolve", "done", jQuery.Callbacks("once memory"), "resolved" ],

                [ "reject", "fail", jQuery.Callbacks("once memory"), "rejected" ],

                [ "notify", "progress", jQuery.Callbacks("memory") ]

            ],

            state = "pending",

            promise = {

                state: function() {

                    return state;

                },

                always: function() {

                    deferred.done( arguments ).fail( arguments );

                    return this;

                },

                then: function( /* fnDone, fnFail, fnProgress */ ) {

                    var fns = arguments;

                    return jQuery.Deferred(function( newDefer ) {

                        jQuery.each( tuples, function( i, tuple ) {

                            var action = tuple[ 0 ],

                                fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];

                            // deferred[ done | fail | progress ] for forwarding actions to newDefer

                            deferred[ tuple[1] ](function() {

                                var returned = fn && fn.apply( this, arguments );

                                if ( returned && jQuery.isFunction( returned.promise ) ) {

                                    returned.promise()

                                        .done( newDefer.resolve )

                                        .fail( newDefer.reject )

                                        .progress( newDefer.notify );

                                } else {

                                    newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );

                                }

                            });

                        });

                        fns = null;

                    }).promise();

                },

                // Get a promise for this deferred

                // If obj is provided, the promise aspect is added to the object

                promise: function( obj ) {

                    return obj != null ? jQuery.extend( obj, promise ) : promise;

                }

            },

            deferred = {};



        // Keep pipe for back-compat

        promise.pipe = promise.then;



        // Add list-specific methods

        jQuery.each( tuples, function( i, tuple ) {

            var list = tuple[ 2 ],

                stateString = tuple[ 3 ];



            // promise[ done | fail | progress ] = list.add

            promise[ tuple[1] ] = list.add;



            // Handle state

            if ( stateString ) {

                list.add(function() {

                    // state = [ resolved | rejected ]

                    state = stateString;



                // [ reject_list | resolve_list ].disable; progress_list.lock

                // 可以看看上篇中lock方法的各种场景调用

                }, tuples[ i ^ 1 ][ 2 ].disable, tuples[ 2 ][ 2 ].lock );

            }



            // deferred[ resolve | reject | notify ]

            deferred[ tuple[0] ] = function() {

                // 如果方法不被借用,那么回调中的this对象为promise,没有触发回调的方法

                deferred[ tuple[0] + "With" ]( this === deferred ? promise : this, arguments );

                return this;

            };

            deferred[ tuple[0] + "With" ] = list.fireWith;

        });



        // Make the deferred a promise

        promise.promise( deferred );



        // Call given func if any

        // 作用于then方法

        if ( func ) {

            func.call( deferred, deferred );

        }



        // All done!

        return deferred;

    }

});
View Code

 

神奇的then方法

在实际项目应用中,一个页面或许有多个ajax请求,你可能会这样做:

$.ajax({ url1, ... });

$.ajax({ url2, ... });

$.ajax({ url3, ... });

...

这样做的缺点:

1、多个ajax同时发送请求,可能会造成服务器压力,对于富应用页面来说,如果请求过多,那是必然的;

2、对于页面底部,或者说首屏不展示给用户浏览的部分需要发送的ajax请求,没有必要让它一开始加载页面后就发送请求,这样会造成页面响应缓慢

 

jQuery递延对象中的then方法好像天生就是为了解决以上问题而设计的,它可以按照顺序依次处理多个异步请求,即第一个请求处理完后,

再处理第二个请求,以此类推,这样既可以减轻服务器压力,又可以先发送首屏(从上到下)页面部分的请求,使页面响应更快

 

来看看一段非常优雅的实例代码

var promiseA = $.get(urlA);

promiseA.always(doneFnA, failFnA, progressFnA);



var promiseB = promiseA.then(function(){

    return $.get(urlB);

});

promiseB.always(doneFnB, failFnB, progressFnB);

或者你也可以这样写,但并不建议:

var promiseB = $.get(urlA).then(function(){

    var state = this.state();

    // 针对第一个ajax请求的处理

    switch (state) {

        case 'resolved' :

            doneFnA();

            break;

        case 'rejected' :

            failFnA();

            break;

        case 'pending' :

            progressA();

            break;

        default: 

            break;

    }

    return $.get(urlB);

});

promiseB.always(doneFnB, failFnB, progressB);
View Code


上面代码是如何运行的呢:

首先发送第一个ajax请求,当promiseA对象执行过resolve(或reject、notify)后,即:第一个请求成功或失败后,将依次执行回调doneFnA

(或failFnA、progressFnA),then中的匿名函数(注意代码的顺序,之前代码顺序有误,把promiseA.always放在了then方法执行之后,现已改过来了),

匿名函数中发送第二个ajax请求,当请求成功或失败后,将执行对应的回调函数(doneFnB或failFnB、progressFnB)

 

衍生后的代码

var promiseA = $.get(urlA);

// 这里添加promiseA的回调



var promiseB = promiseA.then(function(){

    return $.get(urlB);

});

// 这里添加promiseB的回调



var promiseC = promiseB.then(function(){

    return $.get(urlC);

});

// 这里添加promiseC的回调



var promiseD = promiseC.then(function(){

    return $.get(urlD);

});

// 这里添加promiseD的回调

 

再来看看then函数中的构造源码,通过上面的实例分析,相信眼前的你会恍然大悟的

promise = {

    then: function( /* fnDone, fnFail, fnProgress */ ) {

        var fns = arguments;

        // 返回后的promise对象与newDefer对应

        return jQuery.Deferred(function( newDefer ) {

            jQuery.each( tuples, function( i, tuple ) {

                var action = tuple[ 0 ],

                    fn = jQuery.isFunction( fns[ i ] ) && fns[ i ];

                // deferred[ done | fail | progress ] for forwarding actions to newDefer

                // 为第一个递延对象添加回调

                deferred[ tuple[1] ](function() {

                    var returned = fn && fn.apply( this, arguments );



                    // 如果回调返回的是一个递延对象,newDefer将根据这个返回的递延对象的状态来触发行为

                    if ( returned && jQuery.isFunction( returned.promise ) ) {   

                        

                        returned.promise()

                            .done( newDefer.resolve )

                            .fail( newDefer.reject )

                            .progress( newDefer.notify );

                    }

                    // 如果回调返回的不是一个递延对象,newDefer将根据第一个(deferred)递延对象的状态来触发行为

                    else {

                        newDefer[ action + "With" ]( this === promise ? newDefer.promise() : this, fn ? [ returned ] : arguments );

                    }

                });

            });

            fns = null;

        }).promise();

    }

}

 

PS: 如有描述错误,请帮忙指正,如果你们有不明白的地方也可以发邮件给我,

  如需转载,请附上本文地址及出处:博客园华子yjh,谢谢!

 

你可能感兴趣的:(jquery)