Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Zepto源码分析-deferred模块 #14

Open
mominger opened this issue Apr 14, 2015 · 0 comments
Open

Zepto源码分析-deferred模块 #14

mominger opened this issue Apr 14, 2015 · 0 comments

Comments

@mominger
Copy link
Owner

源码注释

//     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用donefail
            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的donefailprogress为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变为0deferred.resolveWith(ctx, val)      //调用主函数的成功
            }
          }
        }

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

      //遍历每个对象
      for ( ; i < len; ++i ) {
         //如果是defif (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规范生命周期

promise 2

Deferred API

deferred 2

Deferred生命周期

deferred 2

Deferred设计

deferred 2

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant