zoukankan      html  css  js  c++  java
  • 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,谢谢!

  • 相关阅读:
    网站安全编程 黑客入侵 脚本黑客 高级语法入侵 C/C++ C# PHP JSP 编程
    【算法导论】贪心算法,递归算法,动态规划算法总结
    cocoa2dx tiled map添加tile翻转功能
    8月30日上海ORACLE大会演讲PPT下载
    【算法导论】双调欧几里得旅行商问题
    Codeforces Round #501 (Div. 3) B. Obtaining the String (思维,字符串)
    Codeforces Round #498 (Div. 3) D. Two Strings Swaps (思维)
    Educational Codeforces Round 89 (Rated for Div. 2) B. Shuffle (数学,区间)
    洛谷 P1379 八数码难题 (BFS)
    Educational Codeforces Round 89 (Rated for Div. 2) A. Shovels and Swords (贪心)
  • 原文地址:https://www.cnblogs.com/yangjunhua/p/3509342.html
Copyright © 2011-2022 走看看