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,谢谢!

  • 相关阅读:
    project b2c_performance / capacity
    WebServer Roxen
    OS + UNIX AIX /etc/services
    但行好事,莫问前程 All In One
    学习什么语言的问题,其实,不是一个问题......
    不想当将军的学生,不是好程序员——数据访问层DAL
    C#如何用Graphics画出一幅图表
    混蛋的面试题——《大话设计模式》读后感
    编程也讲禅,您读过《金刚经》吗?——ADO.NET核心类的灭度与SQLHelper的诞生——十八相送(上)
    C#如何开发扫雷游戏
  • 原文地址:https://www.cnblogs.com/yangjunhua/p/3509342.html
Copyright © 2011-2022 走看看