Zepto源码分析-deferred模块

源码注释

  

//     Zepto.js
//     (c) 2010-2015 Thomas Fuchs
//     Zepto.js may be freely distributed under the MIT license.
//
//     Some code (c) 2005, 2013 jQuery Foundation, Inc. and other contributors

;(function($){
  var slice = Array.prototype.slice

  function Deferred(func) {

    //元组:描述状态、状态切换方法名、对应状态执行方法名、回调列表的关系
      //tuple引自C++/python,和list的区别是,它不可改变 ,用来存储常量集
    var tuples = [
          // action, add listener, listener list, final state
          [ "resolve", "done", $.Callbacks({once:1, memory:1}), "resolved" ],
          [ "reject", "fail", $.Callbacks({once:1, memory:1}), "rejected" ],
          [ "notify", "progress", $.Callbacks({memory:1}) ]
        ],
        state = "pending", //Promise初始状态

        //promise对象,promise和deferred的区别是:
        /*promise只包含执行阶段的方法always(),then(),done(),fail(),progress()及辅助方法state()、promise()等。
          deferred则在继承promise的基础上,增加切换状态的方法,resolve()/resolveWith(),reject()/rejectWith(),notify()/notifyWith()*/
        //所以称promise是deferred的只读副本
        promise = {
            /**
             * 返回状态
             * @returns {string}
             */
          state: function() {
            return state
          },
            /**
             * 成功/失败状态的 回调调用
             * @returns {*}
             */
          always: function() {
            deferred.done(arguments).fail(arguments)
            return this
          },
            /**
             *
             * @returns promise对象
             */
          then: function(/* fnDone [, fnFailed [, fnProgress]] */) {
            var fns = arguments

            //注意,这无论如何都会返回一个新的Deferred只读副本,
            //所以正常为一个deferred添加成功,失败,千万不要用then,用done,fail
            return Deferred(function(defer){
              $.each(tuples, function(i, tuple){
                //i==0: done   i==1: fail  i==2 progress
                var fn = $.isFunction(fns[i]) && fns[i]

                //执行新deferred done/fail/progress
                deferred[tuple[1]](function(){
                    //直接执行新添加的回调 fnDone fnFailed fnProgress
                  var returned = fn && fn.apply(this, arguments)

                    //返回结果是promise对象
                  if (returned && $.isFunction(returned.promise)) {
                     //转向fnDone fnFailed fnProgress返回的promise对象
                     //注意,这里是两个promise对象的数据交流
                      //新deferrred对象切换为对应的成功/失败/通知状态,传递的参数为 returned.promise() 给予的参数值
                    returned.promise()
                      .done(defer.resolve)
                      .fail(defer.reject)
                      .progress(defer.notify)
                  } else {
                    var context = this === promise ? defer.promise() : this,
                        values = fn ? [returned] : arguments
                    defer[tuple[0] + "With"](context, values)//新deferrred对象切换为对应的成功/失败/通知状态
                  }
                })
              })
              fns = null
            }).promise()
          },

            /**
             * 返回obj的promise对象
             * @param obj
             * @returns {*}
             */
          promise: function(obj) {
            return obj != null ? $.extend( obj, promise ) : promise
          }
        },

        //内部封装deferred对象
        deferred = {}

    //给deferred添加切换状态方法
    $.each(tuples, function(i, tuple){
      var list = tuple[2],//$.Callback
          stateString = tuple[3]//   状态 如 resolved

        //扩展promise的done、fail、progress为Callback的add方法,使其成为回调列表
        //简单写法:  promise[‘done‘] = jQuery.Callbacks( "once memory" ).add
        //         promise[‘fail‘] = jQuery.Callbacks( "once memory" ).add  promise[‘progress‘] = jQuery.Callbacks( "memory" ).add
      promise[tuple[1]] = list.add

        //切换的状态是resolve成功/reject失败
        //添加首组方法做预处理,修改state的值,使成功或失败互斥,锁定progress回调列表,
      if (stateString) {
        list.add(function(){
          state = stateString

            //i^1  ^异或运算符  0^1=1 1^1=0,成功或失败回调互斥,调用一方,禁用另一方
        }, tuples[i^1][2].disable, tuples[2][2].lock)
      }

        //添加切换状态方法 resolve()/resolveWith(),reject()/rejectWith(),notify()/notifyWith()
      deferred[tuple[0]] = function(){
        deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments)
        return this
      }
      deferred[tuple[0] + "With"] = list.fireWith
    })

    //deferred继承promise的执行方法
    promise.promise(deferred)

    //传递了参数func,执行
    if (func) func.call(deferred, deferred)

    //返回deferred对象
    return deferred
  }

    /**
     *
     * 主要用于多异步队列处理。
       多异步队列都成功,执行成功方法,一个失败,执行失败方法
       也可以传非异步队列对象

     * @param sub
     * @returns {*}
     */
  $.when = function(sub) {
    var resolveValues = slice.call(arguments), //队列数组 ,未传参数是[]
        len = resolveValues.length,//队列个数
        i = 0,
        remain = len !== 1 || (sub && $.isFunction(sub.promise)) ? len : 0, //子def计数
        deferred = remain === 1 ? sub : Deferred(),//主def,如果是1个fn,直接以它为主def,否则建立新的Def
        progressValues, progressContexts, resolveContexts,
        updateFn = function(i, ctx, val){
          return function(value){
            ctx[i] = this    //this
            val[i] = arguments.length > 1 ? slice.call(arguments) : value   // val 调用成功函数列表的参数
            if (val === progressValues) {
              deferred.notifyWith(ctx, val)  // 如果是通知,调用主函数的通知,通知可以调用多次
            } else if (!(--remain)) {          //如果是成功,则需等成功计数为0,即所有子def都成功执行了,remain变为0,
              deferred.resolveWith(ctx, val)      //调用主函数的成功
            }
          }
        }

      //长度大于1,
    if (len > 1) {
      progressValues = new Array(len) //
      progressContexts = new Array(len)
      resolveContexts = new Array(len)

      //遍历每个对象
      for ( ; i < len; ++i ) {
         //如果是def,
        if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) {
          resolveValues[i].promise()
            .done(updateFn(i, resolveContexts, resolveValues)) //每一个成功
            .fail(deferred.reject)//直接挂入主def的失败通知函数,当某个子def失败时,调用主def的切换失败状态方法,执行主def的失败函数列表
            .progress(updateFn(i, progressContexts, progressValues))
        } else {
          --remain   //非def,直接标记成功,减1
        }
      }
    }

    //都为非def,比如无参数,或者所有子队列全为非def,直接通知成功,进入成功函数列表
    if (!remain) deferred.resolveWith(resolveContexts, resolveValues)
    return deferred.promise()
  }

  $.Deferred = Deferred
})(Zepto)

Promises/A+

  由于deferred是基于Promise规范,我们首先需要理清楚Promises/A+是什么。

  它的规范内容大致如下(此翻译内容引自这里) 

  • 一个promise可能有三种状态:等待(pending)、已完成(fulfilled)、已拒绝(rejected)
  • 一个promise的状态只可能从“等待”转到“完成”态或者“拒绝”态,不能逆向转换,同时“完成”态和“拒绝”态不能相互转换
  • promise必须实现then方法(可以说,then就是promise的核心),而且then必须返回一个promise,同一个promise的then可以调用多次,并且回调的执行顺序跟它们被定义时的顺序一致
  • then方法接受两个参数,第一个参数是成功时的回调,在promise由“等待”态转换到“完成”态时调用,另一个是失败时的回调,在promise由“等待”态转换到“拒绝”态时调用。同时,then可以接受另一个promise传入,也接受一个“类then”的对象或方法,即thenable对象。

先用伪代码来实现其规范内容

      //普通的异步回调写法。
        function fA(){
            var a1,a2;

            //出现异常,调用其他方法
            try{
                fa1(a1);
                fa2(a2);
            }catch(e){
                efa1(a1);
                efa2(a2);
            }
        }

        function fa2(){
            fB();//调用另一个和fA类似的异步回调
        }

        //下面采用Promise规范来改写

       //初始化: 等待状态  pending
        var Promise = {
            status: pending,  //状态
            promise: function(o){
                return {
                    done:done,
                    fail:fail
            }
            },
            //必须申明的then方法
            then:function(fulfilledFn,rejectedFn){
                this.done(fulfilledFn);
                this.fail(rejectedFn);

                //返回promise对象
                return this;
            },

            //当状态切换fulfilled时执行
            done: function(){

            },
            //当状态切换rejected时执行
            fail:function(){

            },

            //切换为已完成状态
            toFulfilled:function(){
                 this.status = ‘fulfilled‘
            },

            //切换为已拒绝状态
            toRejected:function(){
                this.status = ‘rejected‘
            }

        }

        //将函数包装成Promise对象,并注册完成、拒绝链方法
        //通过then
        Promise.promise(fA).then(fa1,efa1).then(fa2,efa2);
        //假定fb里还调用了另一个异步FB,
        //之前fA的异步回调执行到fb方法
        var PA = Promise.promise(fA).then(fa,efa).then(fb,efb);
        //再挂上fB的异步回调
        PA.then(fB).then(fb1,efb1).then(fb2,efb2);

Promise规范生命周期

Deferred用法

Deferred生命周期

Deferred设计

时间: 2024-10-13 12:39:27

Zepto源码分析-deferred模块的相关文章

Zepto源码分析-form模块

源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT license. ;(function($){ /** * 序列表单内容为JSON数组 * 返回类似[{a1:1},{a2:2}]的数组 * @returns {Array} */ $.fn.serializeArray = function() { var name, type, result =

Zepto源码分析-event模块

源码注释 // Zepto.js // (c) 2010-2015 Thomas Fuchs // Zepto.js may be freely distributed under the MIT license. ;(function($){ var _zid = 1, undefined, slice = Array.prototype.slice, isFunction = $.isFunction, isString = function(obj){ return typeof obj

zepto源码分析系列

如果你也开发移动端web,如果你也用zepto,应该值得你看看.有问题请留言. Zepto源码分析-架构 Zepto源码分析-zepto(DOM)模块 Zepto源码分析-callbacks模块 Zepto源码分析-event模块 Zepto源码分析-ajax模块 Zepto源码分析-form模块 Zepto源码分析-deferred模块 Zepto源码分析-动画(fx fx_method)模块 内容一定要200字一定要200字内容一定要200字一定要200字内容一定要200字一定要200字内容

nginx源码分析--nginx模块解析

nginx的模块非常之多,可以认为所有代码都是以模块的形式组织,这包括核心模块和功能模块,针对不同的应用场合,并非所有的功能模块都要被用到,附录A给出的是默认configure(即简单的http服务器应用)下被连接的模块,这里虽说是模块连接,但nginx不会像apache或lighttpd那样在编译时生成so动态库而在程序执行时再进行动态加载,nginx模块源文件会在生成nginx时就直接被编译到其二进制执行文件中,所以如果要选用不同的功能模块,必须对nginx做重新配置和编译.对于功能模块的选

Zepto源码分析之二~三个API

由于时间关系:本次只对这三个API($.camelCase.$.contains.$.each)方法进行分析 第一个方法变量转驼峰:$.camelCase('hello-world-welcome'); 源码: var camelize; /** * 字符串替换 * 使用replace第二个参数带回调 */ camelize = function(str) { return str.replace(/-+(.)?/g, function(match, chr) { return chr ? ch

jQuery 2.0.3 源码分析 Deferred(最细的实现剖析,带图)

转载http://www.cnblogs.com/aaronjs/p/3356505.html Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html ******************构建Deferred对象时候的流程图************************** **********************源码解析********************** 因为callback被剥离出去后,整个deferred

nginx源码分析之模块初始化

在nginx启动过程中,模块的初始化是整个启动过程中的重要部分,而且了解了模块初始化的过程对应后面具体分析各个模块会有事半功倍的效果.在我看来,分析源码来了解模块的初始化是最直接不过的了,所以下面主要通过结合源码来分析模块的初始化过程. 稍微了解nginx的人都知道nginx是高度模块化的,各个功能都封装在模块中,而各个模块的初始化则是根据配置文件来进行的,下面我们会看到nginx边解析配置文件中的指令,边初始化指令所属的模块,指令其实就是指示怎样初始化模块的. 模块初始化框架 模块的初始化主要

最细的实现剖析:jQuery 2.0.3源码分析Deferred

Deferred的概念请看第一篇 http://www.cnblogs.com/aaronjs/p/3348569.html **构建Deferred对象时候的流程图** **源码解析** 因为callback被剥离出去后,整个deferred就显得非常的精简 jQuery.extend({ Deferred:function(){} when:function() )}对于extend的继承这个东东,在之前就提及过jquery如何处理内部jquery与init相互引用this的问题 对于JQ的

兄弟连区块链教程open-ethereum-pool矿池源码分析unlocker模块

兄弟连区块链教程open-ethereum-pool以太坊矿池源码分析unlocker模块open-ethereum-pool以太坊矿池-unlocker模块 unlocker模块配置 json"unlocker": {????"enabled": false,????"poolFee": 1.0,????"poolFeeAddress": "",????"donate": true,?