国产xxxx99真实实拍_久久不雅视频_高清韩国a级特黄毛片_嗯老师别我我受不了了小说

資訊專欄INFORMATION COLUMN

讀Zepto源碼之Deferred模塊

yagami / 3020人閱讀

摘要:為的項,取出來的分別為和,所以上的和方法,調(diào)用的是中的方法,實質(zhì)是往各自的回調(diào)列表中添加回調(diào)函數(shù)。進度回調(diào)函數(shù)數(shù)組。參數(shù)為異步對象的索引值,參數(shù)為對應(yīng)的上下文數(shù)組,即或,為對應(yīng)的回調(diào)函數(shù)數(shù)組,即或。

Deferred 模塊也不是必備的模塊,但是 ajax 模塊中,要用到 promise 風格,必需引入 Deferred 模塊。Deferred 也用到了上一篇文章《讀Zepto源碼之Callbacks模塊 )》介紹的 Callbacks 模塊。

讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡迎star: reading-zepto

源碼版本

本文閱讀的源碼為 zepto1.2.0

Promise/A+ 規(guī)范

規(guī)范的具體內(nèi)容可以參考《Promises/A+》 和對應(yīng)的中文翻譯 《Promise/A+規(guī)范》,這里只簡單總結(jié)一下。

promise 是一個包含兼容 promise 規(guī)范的函數(shù)或?qū)ο螅?b>promise 包含三種狀態(tài) pending 進行中、fulfilled 已完成, rejected 被拒絕,并且必須處于其中一種狀態(tài)。

pending 狀態(tài)可以轉(zhuǎn)換成 fulfilled 狀態(tài)或者 rejected 狀態(tài),但是 fulfilled 狀態(tài)和 rejected 狀態(tài)不能再轉(zhuǎn)換成其他狀態(tài)。

promise 必須包含 then 方法,then 方法可以接收兩個參數(shù),參數(shù)類型都為函數(shù),分別為狀態(tài)變?yōu)?fulfilled 后調(diào)用的 onFulfilled 函數(shù)和 rejected 后調(diào)用的 onRejected 函數(shù)。

大致了解 Promise/A+ 規(guī)范后,對后面源碼的閱讀會有幫助。

Deferred 模塊的整體結(jié)構(gòu)
;(function($){
  function Deferred(func) {
    deferred = {}
    if (func) func.call(deferred, deferred)
    return deferred
  }
  return $.Deferred = Deferred
})(Zepto)

從上面的精簡的結(jié)構(gòu)可以看出,Deferred 是一個函數(shù),函數(shù)的返回值是一個符合 Promise/A+ 規(guī)范的對象,如果 Deferred 有傳遞函數(shù)作為參數(shù),則以 deferred 作為上下文,以 deferred 作為參數(shù)執(zhí)行該函數(shù)。

done、fail、progress、resolve/resolveWith、reject/rejectWith、notify/notifyWith 方法的生成
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 = {
      ...
    }
    deferred = {}
$.each(tuples, function(i, tuple){
  var list = tuple[2],
      stateString = tuple[3]

  promise[tuple[1]] = list.add

  if (stateString) {
    list.add(function(){
      state = stateString
    }, tuples[i^1][2].disable, tuples[2][2].lock)
  }

  deferred[tuple[0]] = function(){
    deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments)
    return this
  }
  deferred[tuple[0] + "With"] = list.fireWith
})
變量解釋

tuples: 用來儲存狀態(tài)切換的方法名,對應(yīng)狀態(tài)的執(zhí)行方法,回調(diào)關(guān)系列表和最終的狀態(tài)描述。

state: 狀態(tài)描述

promise:promise 包含執(zhí)行方法 alwaysthendonefailprogress 和輔助方法 statepromise

deferred: deferred 除了繼承 promise 的方法外,還增加了切換方法, resolveresoveWithrejectrejectWithnotifynotifyWith
?

done、fail和progress的生成
$.each(tuples, function(i, tuple){
  ...
})

方法的生成,通過遍歷 tuples 實現(xiàn)

var list = tuple[2],
    stateString = tuple[3]

promise[tuple[1]] = list.add

list 是工廠方法 $.Callbacks 生成的管理回調(diào)函數(shù)的一系列方法。具體參見上一篇文章《讀Zepto源碼之Callbacks模塊》。注意,tuples 的所有項中的 $Callbacks 都配置了 memory:1 ,即開啟記憶模式,增加的方法都會立即觸發(fā)。包含 resovereject 的項都傳遞了 once:1 ,即回調(diào)列表只能觸發(fā)一次。

stateString 是狀態(tài)描述,只有包含了 resolvereject 的數(shù)組項才具有。

index1 的項,取出來的分別為 donefailprogress ,所以 promise 上的 donefailprogress 方法,調(diào)用的是 Callbacks 中的 add 方法,實質(zhì)是往各自的回調(diào)列表中添加回調(diào)函數(shù)。

狀態(tài)切換
if (stateString) {
  list.add(function(){
    state = stateString
  }, tuples[i^1][2].disable, tuples[2][2].lock)
}

如果 stateString 存在,即包含 resolvereject 的數(shù)組項,則往對應(yīng)的回調(diào)列表中添加切換 state 狀態(tài)的方法,將 state 更改為對應(yīng)方法觸發(fā)后的狀態(tài)。

同時,將狀態(tài)鎖定,即狀態(tài)變?yōu)?resolvedrejected 狀態(tài)后,不能再更改為其他狀態(tài)。這里用來按位異或運算符 ^ 來實現(xiàn)。當 i0 ,即狀態(tài)變?yōu)?resolved 時, i^11tuples[i^1][2].disablerejected 的回調(diào)列表禁用,當 i1 時, i^10 ,將 resolved 的回調(diào)列表禁用。即實現(xiàn)了成功和失敗的狀態(tài)互斥,做得狀態(tài)鎖定,不能再更改。

在狀態(tài)變更后,同時將 tuples[2] 的回調(diào)列表鎖定,要注意 disablelock 的區(qū)別,具體見《讀Zepto源碼之Callbacks模塊》,由于這里用了記憶模式,所以還可以往回調(diào)列表中添加回調(diào)方法,并且回調(diào)方法會立即觸發(fā)。

resolve/resolveWith、reject/rejectWith、notify/notifyWith 方法的生成
deferred[tuple[0] + "With"] = list.fireWith

deferred[tuple[0]] = function(){
  deferred[tuple[0] + "With"](this === deferred ? promise : this, arguments)
  return this
}

這幾個方法,存入在 deferred 對象中,并沒有存入 promise 對象。

resolveWithrejectWithnotifyWith 方法,其實等價于 CallbackfireWith 方法,fireWith 方法的第一個參數(shù)為上下文對象。

從源碼中可以看到 resolverejectnotify 方法,調(diào)用的是對應(yīng)的 With 后綴方法,如果當前上下文為 deferred 對象,則傳入 promise 對象作為上下文。

promise 對象 .state()
state: function() {
  return state
},

state 方法的作用是返回當前的狀態(tài)。

.always()
always: function() {
  deferred.done(arguments).fail(arguments)
  return this
},

always 是一種省事的寫法,即無論成功還是失敗,都會執(zhí)行回調(diào)。調(diào)用的是 deferred 上的 donefail 方法。或許你會有疑惑,donefail 方法,上面的分析中,明明是 promise 的方法,為什么 deferred 對象上也有這兩個方法呢,這個下面會講到。

.promise()
promise: function(obj) {
  return obj != null ? $.extend( obj, promise ) : promise
}

返回 promise 對象,如果 obj 有傳遞,則將 promise 上的方法擴展到 obj 上。

.then()
then: function(/* fnDone [, fnFailed [, fnProgress]] */) {
  var fns = arguments
  return Deferred(function(defer){
    $.each(tuples, function(i, tuple){
      var fn = $.isFunction(fns[i]) && fns[i]
      deferred[tuple[1]](function(){
        var returned = fn && fn.apply(this, arguments)
        if (returned && $.isFunction(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)
        }
      })
    })
    fns = null
  }).promise()
}

promisethen 方法接收三個參數(shù),分別為成功的回調(diào)、失敗的回調(diào)和進度的回調(diào)。

then整體結(jié)構(gòu)

then 簡化后,可以看到以下的結(jié)構(gòu):

return Deferred(function(defer){}).promise()

返回的是 deferred 對象,deferred 對象上的 promise 方法,其實就是 promise 對象上的 promise 方法,所以 then 方法,最終返回的還是 promise 對象。所以 promise 可以這樣一直調(diào)用下去 promise().then().then()....

Deferred 調(diào)用
var fns = arguments
return Deferred(function(defer) {
  ...
})
fns = null

這里的變量 fnsthen 所傳入的參數(shù),即上文提到的三個回調(diào)。

最后的 fns = null ,是釋放引用,讓 JS 引擎可以進行垃圾回收。

Deferred 的參數(shù)是一個函數(shù),上文在分析總體結(jié)構(gòu)的時候,有一句關(guān)鍵的代碼 if (func) func.call(deferred, deferred) 。所以這里的函數(shù)的參數(shù) defer 即為 deferred 對象。

執(zhí)行回調(diào)
$.each(tuples, function(i, tuple){
  var fn = $.isFunction(fns[i]) && fns[i]
  deferred[tuple[1]](function(){
    var returned = fn && fn.apply(this, arguments)
    if (returned && $.isFunction(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)
    }
  })
})

遍歷 tuplestuples 中的順序,跟 then 中規(guī)定 donefailprogress 的回調(diào)順序一致。

所以用 var fn = $.isFunction(fns[i]) && fns[i] 來判斷對應(yīng)位置的參數(shù)是否為 function 類型,如果是,則賦值給 fn

deferred[tuple[1]] 是對應(yīng)的是 donefailprogress 。所以在 then 里,會依次執(zhí)行這三個方法。

var returned = fn && fn.apply(this, arguments)

returnedthen 中三個回調(diào)方法執(zhí)行后返回的結(jié)果。

if (returned && $.isFunction(returned.promise)) {
  returned.promise()
    .done(defer.resolve)
    .fail(defer.reject)
    .progress(defer.notify)
}

如果回調(diào)返回的是 promise 對象,調(diào)用新 promise 對象中的 promise 方法,新 promise 對象切換狀態(tài)時, 并將當前 deferred 對象對應(yīng)的狀態(tài)切換方法傳入,在新 promise 切換狀態(tài)時執(zhí)行。這就實現(xiàn)了兩個 promise 對象的狀態(tài)交流。

var context = this === promise ? defer.promise() : this,
    values = fn ? [returned] : arguments
defer[tuple[0] + "With"](context, values)

如果返回的不是 promise 對象,則判斷 this 是否為 promise ,如果是,則返回 defer.promise() ,修正執(zhí)行的上下文。

然后調(diào)用對應(yīng)的狀態(tài)切換方法切換狀態(tài)。

promise 對象與 deferred 對象
promise.promise(deferred)

從上面的分析中,可以看到,deferred 對象上并沒有donefailprogress 方法,這是從 promise 上擴展來的。

既然已經(jīng)有了一個擁有 promise 對象的所有方法的 deferred 對象,為什么還要一個額外的 promise 對象呢?

promise 對象上沒有狀態(tài)切換方法,所以在 then 中,要綁定上下文的時候時候,綁定的都是 promise 對象,這是為了避免在執(zhí)行的過程中,將執(zhí)行狀態(tài)改變。

$.when
$.when = function(sub) {
    var resolveValues = slice.call(arguments),
        len = resolveValues.length,
        i = 0,
        remain = len !== 1 || (sub && $.isFunction(sub.promise)) ? len : 0,
        deferred = remain === 1 ? sub : Deferred(),
        progressValues, progressContexts, resolveContexts,
        updateFn = function(i, ctx, val){
          return function(value){
            ctx[i] = this
            val[i] = arguments.length > 1 ? slice.call(arguments) : value
            if (val === progressValues) {
              deferred.notifyWith(ctx, val)
            } else if (!(--remain)) {
              deferred.resolveWith(ctx, val)
            }
          }
        }

    if (len > 1) {
      progressValues = new Array(len)
      progressContexts = new Array(len)
      resolveContexts = new Array(len)
      for ( ; i < len; ++i ) {
        if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) {
          resolveValues[i].promise()
            .done(updateFn(i, resolveContexts, resolveValues))
            .fail(deferred.reject)
            .progress(updateFn(i, progressContexts, progressValues))
        } else {
          --remain
        }
      }
    }
    if (!remain) deferred.resolveWith(resolveContexts, resolveValues)
    return deferred.promise()
  }

when 方法用來管理一系列的異步隊列,如果所有的異步隊列都執(zhí)行成功,則執(zhí)行成功方法,如果有一個異步執(zhí)行失敗,則執(zhí)行失敗方法。這個方法也可以傳入非異步方法。

一些變量
var resolveValues = slice.call(arguments),
    len = resolveValues.length,
    i = 0,
    remain = len !== 1 || (sub && $.isFunction(sub.promise)) ? len : 0,
    deferred = remain === 1 ? sub : Deferred(),
    progressValues, progressContexts, resolveContexts,

resolveValues:所有的異步對象,用 slice 轉(zhuǎn)換成數(shù)組形式。

len: 異步對象的個數(shù)。

remain: 剩余個數(shù)。這里還有個判斷,是為了確定只有一個參數(shù)時,這個參數(shù)是不是異步對象,如果不是,則 remain 初始化為 0 。其他情況,初始化為當前的個數(shù)。

i: 當前異步對象執(zhí)行的索引值。

deferred: deferred 對象,如果只有一個異步對象(只有一個參數(shù),并且不為異步對象時, remain0 ),則直接使用當前的 deferred 對象,否則創(chuàng)建一個新的 deferred 對象。

progressValues: 進度回調(diào)函數(shù)數(shù)組。

progressContexts: 進度回調(diào)函數(shù)綁定的上下文數(shù)組

resolveContexts: 成功回調(diào)函數(shù)綁定的上下文數(shù)組

updateFn
updateFn = function(i, ctx, val){
  return function(value){
    ctx[i] = this
    val[i] = arguments.length > 1 ? slice.call(arguments) : value
    if (val === progressValues) {
      deferred.notifyWith(ctx, val)
    } else if (!(--remain)) {
      deferred.resolveWith(ctx, val)
    }
  }
}

updateFn 方法,在每個異步對象執(zhí)行 resolve 方法和 progress 方法時都調(diào)用。

參數(shù) i 為異步對象的索引值,參數(shù) ctx 為對應(yīng)的上下文數(shù)組,即 resolveContextsresolveContextsval 為對應(yīng)的回調(diào)函數(shù)數(shù)組,即 progresValuesresolveValues

if (val === progressValues) {
    deferred.notifyWith(ctx, val)
}

如果為 progress 的回調(diào),則調(diào)用 deferrednotifyWith 方法。

else if (!(--remain)) {
  deferred.resolveWith(ctx, val)
}

否則,將 remain 減少 1,如果回調(diào)已經(jīng)執(zhí)行完畢,則調(diào)用 deferredresolveWith 方法。

依次處理異步對象
if (len > 1) {
  progressValues = new Array(len)
  progressContexts = new Array(len)
  resolveContexts = new Array(len)
  for ( ; i < len; ++i ) {
    if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) {
      resolveValues[i].promise()
        .done(updateFn(i, resolveContexts, resolveValues))
        .fail(deferred.reject)
        .progress(updateFn(i, progressContexts, progressValues))
    } else {
      --remain
    }
  }
}

首先初始化 progressValuesprogressContextsresolveContexts ,數(shù)組長度為異步對象的長度。

if (resolveValues[i] && $.isFunction(resolveValues[i].promise)) {
  resolveValues[i].promise()
    .done(updateFn(i, resolveContexts, resolveValues))
    .fail(deferred.reject)
    .progress(updateFn(i, progressContexts, progressValues))
}

如果為 promise 對象,則調(diào)用對應(yīng)的 promise 方法。

else {
  --remain
}

如果不是 promise 對象,則將 remian 減少 1

if (!remain) deferred.resolveWith(resolveContexts, resolveValues)
return deferred.promise()

如果無參數(shù),或者參數(shù)不是異步對象,或者所有的參數(shù)列表都不是異步對象,則直接調(diào)用 resoveWith 方法,調(diào)用成功函數(shù)列表。

最后返回的是 promise 對象。

系列文章

讀Zepto源碼之代碼結(jié)構(gòu)

讀 Zepto 源碼之內(nèi)部方法

讀Zepto源碼之工具函數(shù)

讀Zepto源碼之神奇的$

讀Zepto源碼之集合操作

讀Zepto源碼之集合元素查找

讀Zepto源碼之操作DOM

讀Zepto源碼之樣式操作

讀Zepto源碼之屬性操作

讀Zepto源碼之Event模塊

讀Zepto源碼之IE模塊

讀Zepto源碼之Callbacks模塊

參考

Zepto源碼分析-deferred模塊

Promises/A+

Promise/A+規(guī)范

jQuery的deferred對象詳解

License

最后,所有文章都會同步發(fā)送到微信公眾號上,歡迎關(guān)注,歡迎提意見:

作者:對角另一面

文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/84481.html

相關(guān)文章

  • Zepto源碼Ajax模塊

    摘要:私有變量用來臨時存放配置中的,即請求成功后執(zhí)行的回調(diào)函數(shù)名,該配置可以為類型。是根據(jù)配置得出的回調(diào)函數(shù)名。接下來,將的占位符,替換成回調(diào)函數(shù)名,最后將插入到頁面中,發(fā)送請求。 Ajax 模塊也是經(jīng)常會用到的模塊,Ajax 模塊中包含了 jsonp 的現(xiàn)實,和 XMLHttpRequest 的封裝。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡迎star: reading-...

    Crazy_Coder 評論0 收藏0
  • Zepto源碼Stack模塊

    摘要:讀源碼系列文章已經(jīng)放到了上,歡迎源碼版本本文閱讀的源碼為改寫原有的方法模塊改寫了以上這些方法,這些方法在調(diào)用的時候,會為返回的結(jié)果添加的屬性,用來保存原來的集合。方法的分析可以看讀源碼之模塊。 Stack 模塊為 Zepto 添加了 addSelf 和 end 方法。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡迎star: reading-zepto 源碼版本 本文閱讀的...

    crossea 評論0 收藏0
  • Zepto源碼assets模塊

    摘要:模塊是為解決移動版加載圖片過大過多時崩潰的問題。因為沒有處理過這樣的場景,所以這部分的代碼解釋不會太多,為了說明這個問題,我翻譯了這篇文章作為附文怎樣處理移動端對圖片資源的限制,更詳細地解釋了這個模塊的應(yīng)用場景。 assets 模塊是為解決 Safari 移動版加載圖片過大過多時崩潰的問題。因為沒有處理過這樣的場景,所以這部分的代碼解釋不會太多,為了說明這個問題,我翻譯了《How to...

    thursday 評論0 收藏0
  • Zepto源碼Gesture模塊

    摘要:模塊基于上的事件的封裝,利用屬性,封裝出系列事件。這個判斷需要引入設(shè)備偵測模塊。然后是監(jiān)測事件,根據(jù)這三個事件,可以組合出和事件。其中變量對象和模塊中的對象的作用差不多,可以先看看讀源碼之模塊對模塊的分析。 Gesture 模塊基于 IOS 上的 Gesture 事件的封裝,利用 scale 屬性,封裝出 pinch 系列事件。 讀 Zepto 源碼系列文章已經(jīng)放到了github上,歡...

    coolpail 評論0 收藏0
  • Zepto源碼Form模塊

    摘要:模塊處理的是表單提交。表單提交包含兩部分,一部分是格式化表單數(shù)據(jù),另一部分是觸發(fā)事件,提交表單。最終返回的結(jié)果是一個數(shù)組,每個數(shù)組項為包含和屬性的對象。否則手動綁定事件,如果沒有阻止瀏覽器的默認事件,則在第一個表單上觸發(fā),提交表單。 Form 模塊處理的是表單提交。表單提交包含兩部分,一部分是格式化表單數(shù)據(jù),另一部分是觸發(fā) submit 事件,提交表單。 讀 Zepto 源碼系列文章已...

    陳江龍 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<