zoukankan      html  css  js  c++  java
  • 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设计

  • 相关阅读:
    bootstrap模版
    spark
    断点
    如何让数据动起来?Python动态图表制作一览。
    证据就在代码里
    windows下oracle的ora-27100错误
    SQL优化 | MySQL问题处理案例分享三则
    MySQL安装好之后本地可以连接,远程连接卡死
    MySQL千万级大表在线变更表结构
    ORA-39006错误原因及解决办法
  • 原文地址:https://www.cnblogs.com/mominger/p/4411632.html
Copyright © 2011-2022 走看看