• jQuery源码分析(九) 异步队列模块 Deferred 详解


    deferred对象就是jQuery的回调函数解决方案,它解决了如何处理耗时操作的问题,比如一些Ajax操作,动画操作等。(P.s:紧跟上一节:https://www.cnblogs.com/greatdesert/p/11433365.html的内容)

    异步队列有三种状态:待定(pending)、成功(resolved)和失败(rejected),初始时处于pending状态

    我们可以使用jQuery.Deferred创建一个异步队列,返回一个对象,该对象含有如下操作:

    • done(fn/arr)                     ;添加成功回调函数,当异步队列处于成功状态时被调用,参数同:jQuery.Callbacks(flags).add(fn/arr)
    • fail(fn/arr)                           ;添加失败回调函数,参数同上
    • progress(fn/arr)                         ;添加消息回调函数,参数同上
    • then(donefn/arr,failfn/arr,profn/arr)     ;同时添加成功回调函数、失败回调函数和消息回调函数
    • always(fn/arr)                       ;添加回调函数到doneList和failList中,即保存两份引用,当异步队列处于成功或失败状态时被调用    ;参数可以是函数、函数列表
    • state()                                  ;返回异步队列的当前状态、返回pending、resolved或者rejected
    • promise(obj)                           ;如果设置了obj参数对象则为obj对象增加异步队列的方法并返回。如果未设置参数obj,则返回当前Deferred对象的只读副本

    如果调用$.Diferred()创建一个异步队列,返回后的对象可以调用如下几个方法:

     writer by:大沙漠 QQ:22969969

    • resolve(args)                      ;使用指定的参数调用所有的成功回调函数,异步队列进入成功状态,之后就无法再调用
    • reject(args)                           ;使用指定的参数调用所有的失败回调函数,异步队列进入失败状态
    • notify(args)                              ;使用指定的参数调用所有的消息回调函数
    • resolveWith(context,args)               ;使用指定的上下文和参数调用所有的成功回调函数,异步队列进入成功状态
    • rejectWith(context,args)               ;使用指定的上下文和参数调用所有的失败回调函数,异步队列进入失败状态
    • notifyWith(context,args)               ;使用指定的上下文和参数调用所有的消息回调函数

    是不是和上一节介绍的Callbacks的fire和fireWith很像,Defferred内部就是通过通过Callback()回调函数列表来实现的,例如:

    <!DOCTYPE html>
    <html lang="en">
    <head>
        <meta charset="UTF-8">
        <title>Document</title>
        <script src="http://libs.baidu.com/jquery/1.7.1/jquery.js"></script>
    </head>
    <body>
    <script>
        function f1(x){console.log('f1 '+x);}
        function f2(x){console.log('f2 '+x);}
        function f3(x){console.log('f3 '+x);}
    
        var t = $.Deferred();                   //创建异步队列
        t.done(f1).fail(f2).progress(f3);       //添加成功回调函数、失败回调函数和消息列表回调函数。等同于t.then(f1,f2,f3); 
        t.notify('test');                       //触发所有消息函数列表,输出:f3 test
        t.resolve('done');                      //触发所有成功回调函数,输出:done
        t.reject('fail');                       //没有输出,触发成功回调函数后失败回调函数列表就会被禁用,反过来也如此
        t.progress(f1);                         //输出:f1 test。这是因为消息回调函数之前已经被调用过,保存了上下文和参数了,如果之前没有调用,这里就没有输出。
    </script>
    </body>
    </html>

    输出如下:

    源码分析


    异步队列内部的实现原理就是通过jQuery.Callbacks定义三个回调函数列表,分别存储成功、失败、消息回调函数,然后返回一个对象,在该对象上设置done、fail和progress,分别对应这三个回调函数列表的add方法,这样就实现分别添加不同状态的回调函数了

    $.Deferred是通过jQuery.extend函数直接挂载到jQuery里的,结构如下:

    jQuery.extend({
    
        Deferred: function( func ) {
            var doneList = jQuery.Callbacks( "once memory" ),            //成功回调函数列表
                failList = jQuery.Callbacks( "once memory" ),            //失败回调函数列表
                progressList = jQuery.Callbacks( "memory" ),             //消息回调函数列表  ;这三个回调函数列表就是存储我们添加的触发函数的
                state = "pending",                                       //初始状态:pending
                lists = {                                                //后面的方法会遍历变量list中的属性名来为异步队列添加方法deffred.resolve()、resolveWith()、reject()、rejectWith()、notify()和notifyWith()
                    resolve: doneList,
                    reject: failList,
                    notify: progressList
                },
                promise = {                                                //异步队列的只读副本
                    /**/
                },
                deferred = promise.promise({}),                                //异步队列
                key;
    
            for ( key in lists ) {                                            //添加触发成功、失败、消息回调函数的方法,执行后deferred对象就多了resolve()、resolveWith()、reject()、rejectWith()、notify()和notifyWith()方法。
                deferred[ key ] = lists[ key ].fire;
                deferred[ key + "With" ] = lists[ key ].fireWith;
            }
    
            // Handle state
            deferred.done( function() {                                        //添加三个成功回调函数,分别用于设置状态为成功(resolved),禁用失败列表函数列表和锁定消息回调函数列表
                state = "resolved";
            }, failList.disable, progressList.lock ).fail( function() {        //添加三个失败回调韩素,分别用于设置状态为失败(rejected),禁用成功列表函数列表和锁定消息回调函数列表
                state = "rejected";
            }, doneList.disable, progressList.lock );
    
            // Call given func if any
            if ( func ) {
                func.call( deferred, deferred );
            }
    
            // All done!
            return deferred;                                                //返回异步队列deffrred
        },
        //...
    })

    可以看到$.Deferred最后返回的是deferred这个布局变量,而deferred是promise.promise({})的返回值,我们来看看promise的实现方法:

    promise = {                        //异步队列的只读副本
        done: doneList.add,                            //添加成功回调函数,引用对应的回调函数列表的callbacks.add(fn/arr)方法,下同
        fail: failList.add,                            //添加失败回调函数
        progress: progressList.add,                    //添加消息回调函数
    
        state: function() {                            //返回异步队列的当前状态:pending、resolved、rejected之一
            return state;
        },
    
        // Deprecated
        isResolved: doneList.fired,
        isRejected: failList.fired,
    
        then: function( doneCallbacks, failCallbacks, progressCallbacks ) {    //同时添加成功回调函数、失败回调函数和消息回调函数
            deferred.done( doneCallbacks ).fail( failCallbacks ).progress( progressCallbacks );
            return this;
        },
        always: function() {                                                //添加回调函数到doneList和failList中,即保存两份引用,当异步队列处于成功或失败状态时被调用
            deferred.done.apply( deferred, arguments ).fail.apply( deferred, arguments );
            return this;
        },
        pipe: function( fnDone, fnFail, fnProgress ) {                        //过滤当前异步队列的状态和参数,并返回一个新的异步队列的副本,一般用不到
            return jQuery.Deferred(function( newDefer ) {
                jQuery.each( {
                    done: [ fnDone, "resolve" ],
                    fail: [ fnFail, "reject" ],
                    progress: [ fnProgress, "notify" ]
                }, function( handler, data ) {
                    var fn = data[ 0 ],
                        action = data[ 1 ],
                        returned;
                    if ( jQuery.isFunction( fn ) ) {
                        deferred[ handler ](function() {
                            returned = fn.apply( this, arguments );
                            if ( returned && jQuery.isFunction( returned.promise ) ) {
                                returned.promise().then( newDefer.resolve, newDefer.reject, newDefer.notify );
                            } else {
                                newDefer[ action + "With" ]( this === deferred ? newDefer : this, [ returned ] );
                            }
                        });
                    } else {
                        deferred[ handler ]( newDefer[ action ] );
                    }
                });
            }).promise();
        },
        // Get a promise for this deferred
        // If obj is provided, the promise aspect is added to the object
        promise: function( obj ) {                            //如果设置了obj参数对象则为obj对象增加异步队列的方法并返回。如果未设置参数obj,则返回当前Deferred对象的只读副本,
            if ( obj == null ) {                                //如果obj为空
                obj = promise;                                        //则返回promise对象(是上一层作用域链的promise对象)
            } else {
                for ( var key in promise ) {                    //否则遍历promise
                    obj[ key ] = promise[ key ];                    //将promise的所有方法、属性保存到obj中
                }
            }
            return obj;                                            //最后返回obj
        }
    },

    $.Deferred就是对Callbacks回调函数列表的管理而已,产生了一个新的$.Defferred接口,内部添加了一个state表示异步队列的状态。

  • 相关阅读:
    file_get_contents抓取远程URL内容
    Xshell下VI打开文件中文乱码解决
    YII实现Memcache故障转移的配置办法
    Nginx实现多重IF判断的办法
    CentOS安装NodeJS v0.10.25 + Express
    一个小玩意 PHP实现微信红包金额拆分试玩
    Web Service测试利器 Postman
    CentOS安装Git
    PHP导出CSV UTF-8转GBK不乱码的解决办法
    configure: error: C++ compiler cannot create executables
  • 原文地址:https://www.cnblogs.com/greatdesert/p/11454359.html
Copyright © 2020-2023  润新知