zoukankan      html  css  js  c++  java
  • jQuery1.9.1源码分析Deferred对象和Promise对象

    jQuery.extend({
            Deferred: function(func) {
                // 数据集中管理
                var tuples = [
                    ['resolve', 'done', jQuery.Callbacks('once memory'), 'resolved'],
                    ['reject', 'fail', jQuery.Callbacks('once memory'), 'rejected'],
                    ['notify', 'progress', jQuery.Callbacks('memory')]
                ],
                    state = 'pending',
                    promise = {
                        /**
                         * 返回一个字符串,代表Deferred(延迟)对象的当前状态
                         *
                         * @returns {string} "pending"( Deferred对象是尚未完成状态) || "rejected"(Deferred对象是在被拒绝的状态) || "resolved"(Deferred对象是在解决状态)
                         */
                        state: function() {
                            return state;
                        },
                        /**
                         * 当Deferred(延迟)对象解决或拒绝时,调用添加处理程序
                         * 即如果调用后不管成功还是失败,都会执行该回调
                         */
                        always: function() {
                            deferred.done(arguments).fail(arguments);
                            return this;
                        },
                        /**
                         * 
                         * then方法会返回一个新的Deferred对象
                         * 如果then方法的参数是deferred对象,
                         * 上一链的旧deferred会调用[ done | fail | progress ]方法注册回调,该回调内容是:执行when方法对应的参数回调(fnDone, fnFail, fnProgress)。
                         * 1)如果参数回调执行后返回的结果是一个deferred对象,我们就给该deferred对象相应的回调列表添加回调,该回调是触发when方法返回的新deferred对象的成功,失败,处理中(done,fail,progress)的回调列表中的所有回调。
                         * 当我们再给then方法进行链式地添加回调操作(done,fail,progress,always,then)时,就是给新deferred对象注册回调到相应的回调列表。
                         * 如果我们then参数fnDoneDefer, fnFailDefer, fnProgressDefer得到了解决,就会执行后面链式添加回调操作中的参数函数。
                         * 
                         * 2)如果参数回调执行后返回的结果不是deferred对象,就立刻触发新deferred对象相应回调列表的所有回调。
                         * 当我们再给then方法进行链式地添加回调操作(done,fail,progress,always,then)时,就会立刻触发我们添加的相应的回调。
                         * 
                         * 可以多个then连续使用,此功能相当于顺序调用异步回调。
                         *
                         * @example
                         * $.ajax({
                               url: 't2.html',
                               dataType: 'html',
                               data: {
                                  d: 4
                               }
                            }).then(function(){
                                console.log('success');
                            },function(){
                                console.log('failed');
                            }).then(function(){
                                console.log('second');
                                return $.ajax({
                                    url: 'jquery-1.9.1.js',
                                    dataType: 'script'
                                });
                            }, function(){
                                console.log('second f');
                                return $.ajax({
                                    url: 'jquery-1.9.1.js',
                                    dataType: 'script'
                                });
                            }).then(function(){
                                console.log('success2');
                            },function(){
                                console.log('failed2');
                            });
                            @notification 当前面的deferred没有被解决即失败,后面的就会执行失败回调,如果then方法中传了两个可返回deferred对象的回调,上一个deferred失败就会触发第二个返回的deferred,如果只传了一个返回deferred的回调,则后面都会报错。后面的失败却不会影响前面的。
                         * @returns {*}
                         */
                        then: function( /* fnDone, fnFail, fnProgress */ ) {
                            var fns = arguments;
                            // 返回一个新的Deferred对象的promise对象
                            return jQuery.Deferred(function(newDefer) {
                                // newDefer其实就是一个新的deferred对象
                                jQuery.each(tuples, function(i, tuple) {
                                    var
                                    // "resolve" | "reject" | "notify"
                                    action = tuple[0],
                                        fn = jQuery.isFunction(fns[i]) && fns[i];
    
                                    // 运行deferred[ done | fail | progress ]方法,
                                    // 将回调函数添加到相应回调列表
                                    deferred[tuple[1]](function() {
                                        // 执行当前参数回调并获取其返回值
                                        var returned = fn && fn.apply(this, arguments);
                                        // 如果returned有返回值且有promise方法,
                                        // 说明是一个deferred对象,
                                        // 则将newDefer对象的三个回调列表的触发器添加到returned对象的相应列表中
                                        if (returned && jQuery.isFunction(returned.promise)) {
                                            returned.promise().
                                            done(newDefer.resolve).
                                            fail(newDefer.reject).
                                            progress(newDefer.notify);
                                        } else {
                                            // 否则就触发newDefer对象的相应回调列表触发器
                                            // 同时确保this指向newDefer的promise对象
                                            // 要是这个newDefer有内容触发需要再在后面将回调添加到响应列表中,
                                            // 所以这个是执行下一个deferred的操作
                                            newDefer[action + 'With'](this === promise ? newDefer.promise() : this, fn ? [returned] : arguments);
                                        }
                                    });
                                });
                                // 销毁对象
                                fns = null;
                            }).promise();
                        },
                        /*
                         如果有参数返回参数对象继承了promise对象属性的对象,
                         否则返回该Deferred对象中的promise对象
                         */
                        promise: function(obj) {
                            return obj != null ? jQuery.extend(obj, promise) : promise;
                        }
                    },
                    deferred = {};
    
                // 备份原始对象
                promise.pipe = promise.then;
    
                // 给deferred对象添加方法
                jQuery.each(tuples, function(i, tuple) {
                    var
                    // jQuery.Callbacks()
                    list = tuple[2],
                        // "resolved" or "rejected"
                        stateString = tuple[3];
    
                    // 给promise对象添加"done", "fail", "progress"方法
                    // 当使用这些方法实际上就是给所在回调列表添加回调
                    // 注意:list.add方法里面的this已经指向了promise
                    // 因此可以deferred.done(arguments).fail(arguments)的链式操作
                    promise[tuple[1]] = list.add;
    
                    // 如果是"resolved"或者"rejected"
                    if (stateString) {
                        // 给相应的回调列表添加以下三个回调函数,回调列表状态机
                        // 第一个是将异步队列状态传给state变量
                        // 第二个方法是将其他状态的列表禁用
                        // 第三个是锁定“progress”的回调列表
                        // 例如是“resolved”则禁用“rejected”的回调列表,
                        // 锁定“progress”的回调列表
                        list.add(function() {
                            state = stateString;
                        }, tuples[i ^ 1][2].disable, tuples[2][2].lock);
                    }
    
                    // 添加deferred[ resolve | reject | notify ]方法
                    deferred[tuple[0]] = function() {
                        // 实际上是运行deferred[ resolveWith | rejectWith | notifyWith ]方法
                        // 同时确保上下文是deferred对象
                        deferred[tuple[0] + 'With'](this === deferred ? promise : this, arguments);
                        // 链式操作
                        return this;
                    };
                    // 添加deferred[ resolveWith | rejectWith | notifyWith ]方法
                    // 这些方法就是所在回调列表的fireWith方法
                    // 通过给定上下文触发列表所有回调函数
                    deferred[tuple[0] + 'With'] = list.fireWith;
                });
    
                // 给deferred对象添加promise对象的所有属性
                promise.promise(deferred);
    
                if (func) {
                    // 运行该函数,this和arguments都是deferred对象
                    func.call(deferred, deferred);
                }
    
                // 返回deferred对象
                // 该对象现有 [ resolve | reject | notify | resolveWith | rejectWith | notifyWith ]
                // 以及从promise继承的 [ done | fail | then | promise | pipe | always | progress | state ]
                // 这些方法
                return deferred;
            },
            /**
             * 对多个deferred对象进行并行化操作,当所有deferred对象都得到解决就执行后面添加的相应回调。
             */
            when: function(subordinate /* , ..., subordinateN */ ) {
                var i = 0,
                    // 把arguments转换成数组
                    resolveValues = core_slice.call(arguments),
                    // 参数的长度
                    length = resolveValues.length,
                    // 如果长度不等于1或者第一个参数是deferred对象,
                    // 返回true,最后返回正常长度
                    // 否则返回0
                    // 说明参数必须是deferred对象,而remaining是记录执行剩余的长度
                    remaining = length !== 1 || (subordinate && jQuery.isFunction(subordinate.promise)) ? length : 0,
                    // 主要的Deferred对象。如果resolveValues只有一个Deferred对象
                    // 使用该对象,否则新建一个Deferred对象
                    deferred = remaining === 1 ? subordinate : jQuery.Deferred(),
                    // 当为resolve或者progress的情况时的处理函数
                    updateFunc = function(i, contexts, values) {
                        return function(value) {
                            contexts[i] = this;
                            // values是when参数数组或者progressValues
                            // 如果参数大于一,values[i]为参数数组
                            // 以$.ajax为例,$.ajax的解决回调的参数values[i] = [response, 'success', jqXHR]
                            // 所以在下次我们给列表添加回调时,回调的参数类似于这样[firsetAjaxCallbackArgs, secondAjaxCallbackArgs]
                            // firsetAjaxCallbackArgs为第一个ajax的[resposne, 'success', jqXHR]
                            // 以此类推
                            /**
                             * $.when(
                                   $.ajax({
                                    url: 't2.html'
                                }),
                                 $.ajax({
                                    url: 'jquery-1.9.1-study.js'
                                })
                                ).then(function(                    FirstAjaxSuccessCallbackArgs, SecondAjaxSuccessCallbackArgs){
                                     console.log('success');
                                }, function(){
                                    console.log('failed');
                                });
                             */
                            values[i] = arguments.length > 1 ? core_slice.call(arguments) : value;
                            // progress
                            if (values === progressValues) {
                                deferred.notifyWith(contexts, values);
    
                                // 将remaining减一,说明该when参数的deferred已经回调注册完毕
                                // 如果这时remaining小于等于0,
                                // 说明我们正在解决最后一个参数deferred,
                                // 就立刻触发deferred的回调,注意这里的deferred不是参数的deferred
                                // 这里就达到了并行的效果了
                            } else if (!(--remaining)) {
                                // deferred需要在外部给回调列表注册回调
                                // resolve
                                deferred.resolveWith(contexts, values);
                            }
                        };
                    },
                    progressValues, progressContexts, resolveContexts;
    
                // 当至少有两个Deferred对象时
                if (length > 1) {
                    progressValues = new Array(length);
                    progressContexts = new Array(length);
                    resolveContexts = new Array(length);
                    for (; i < length; i++) {
                        // 遍历,如果是deferred对象,给when参数中当前的deferred的回调列表添加回调
                        if (resolveValues[i] && jQuery.isFunction(resolveValues[i].promise)) {
                            resolveValues[i].promise()
                                .done(updateFunc(i, resolveContexts, resolveValues))
                                .fail(deferred.reject)
                                .progress(updateFunc(i, progressContexts, progressValues));
                        } else {
                            // 否则不是deferred对象直接将remaining的长度-1
                            --remaining;
                        }
                    }
                }
    
                // 当remaining为0的时候,也就是length===1或者when参数没有deferred对象时,立刻触发
                if (!remaining) {
                    deferred.resolveWith(resolveContexts, resolveValues);
                }
    
                // 返回promise对象
                return deferred.promise();
            }
        });
    

      

     这有一篇有关Deferred的讲得很好的文章,阮老师的:http://www.ruanyifeng.com/blog/2011/08/a_detailed_explanation_of_jquery_deferred_object.html

    可以帮助更好了解Deferred对象的作用。

    USAGE:

     1 下面是部分Deferred对象的API使用方法:
     2 1.deferred.done(doneCallbacks[,doneCallbacks])与deferred.fail(failCallbacks[,failCallbacks])
     3 
     4 一旦jQuery.get方法返回一个jqXHR对象,这是从一个递延所得,可以附加的成功和失败回调使用deferrred.done()和deferred.fail()方法。
     5 
     6 $.get("test.php")
     7   .done(function(){ alert("$.get succeeded"); })//延迟成功
     8   .fail(function(){ alert("$.get failed!"); });//延迟失败
     9 
    10 
    11 2.deferred.then(doneCallbacks,failCallbacks[, progressCallbacks])
    12 
    13 一旦jQuery.get方法返回一个来自延迟的对象的jqXHR对象,我们可以附加一个成功回调使用.then方法。
    14 
    15 $.get("test.php").then(
    16     function(){ alert("$.get succeeded"); },
    17     function(){ alert("$.get failed!"); }
    18 );
    19 
    20 
    21 3.deferred.always(alwaysCallbacks,[alwaysCallbacks])
    22 
    23 jQuery.get()方法返回一个来自一个延迟的对象的jqXHR对象,我们可以附加一个成功和错误使用deferred.always()方法的回调。
    24 
    25 $.get("test.php").always( function() { 
    26   alert("$.get completed with success or error callback arguments"); 
    27 } );
    28 
    29 1.jQuery.when(deferreds)
    30 
    31 $.when( $.ajax("test.aspx") ).then(function(ajaxArgs){ 
    32      alert(ajaxArgs[1]); /* ajaxArgs is [ "success", statusText, jqXHR ] */
    33 });
    34 
    35 $.when( { testing: 123 } ).done(
    36    function(x){ alert(x.testing); } /* alerts "123" */
    37 );
    38 
    39 $.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1,  a2){
    40     /* a1 and a2 are arguments resolved for the 
    41         page1 and page2 ajax requests, respectively */
    42    var jqXHR = a1[2]; /* arguments are [ "success", statusText, jqXHR ] */
    43    if ( /Whip It/.test(jqXHR.responseText) ) {
    44       alert("First page has 'Whip It' somewhere.");
    45    }
    46 });
    47 
    48 $.when($.ajax("/page1.php"), $.ajax("/page2.php"))
    49   .then(myFunc, myFailure);
  • 相关阅读:
    Winform DataGridView单元格的提示信息
    Winform DataGridView添加列头checkbox
    C#解析Json
    Winform 下拉框绑定问题
    Excel数据复制到Winform控件ListView
    ListView 复制到剪切板
    C#定时器
    Winform程序只允许运行一个程序实例
    集合
    java中的锁
  • 原文地址:https://www.cnblogs.com/webFrontDev/p/3078703.html
Copyright © 2011-2022 走看看