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設計


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM