成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專欄INFORMATION COLUMN

讀Zepto源碼之Deferred模塊

yagami / 3024人閱讀

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

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

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

源碼版本

本文閱讀的源碼為 zepto1.2.0

Promise/A+ 規(guī)范

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

promise 是一個(gè)包含兼容 promise 規(guī)范的函數(shù)或?qū)ο螅?b>promise 包含三種狀態(tài) pending 進(jìn)行中、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 方法可以接收兩個(gè)參數(shù),參數(shù)類型都為函數(shù),分別為狀態(tài)變?yōu)?fulfilled 后調(diào)用的 onFulfilled 函數(shù)和 rejected 后調(diào)用的 onRejected 函數(shù)。

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

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

從上面的精簡(jiǎn)的結(jié)構(gòu)可以看出,Deferred 是一個(gè)函數(shù),函數(shù)的返回值是一個(gè)符合 Promise/A+ 規(guī)范的對(duì)象,如果 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: 用來儲(chǔ)存狀態(tài)切換的方法名,對(duì)應(yīng)狀態(tài)的執(zhí)行方法,回調(diào)關(guān)系列表和最終的狀態(tài)描述。

state: 狀態(tài)描述

promise:promise 包含執(zhí)行方法 always 、thendone、 failprogress 和輔助方法 statepromise

deferred: deferred 除了繼承 promise 的方法外,還增加了切換方法, resolve 、resoveWithreject 、 rejectWithnotify 、 notifyWith 。
?

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

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

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

promise[tuple[1]] = list.add

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

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

index1 的項(xiàng),取出來的分別為 done 、 failprogress ,所以 promise 上的 done、 failprogress 方法,調(diào)用的是 Callbacks 中的 add 方法,實(shí)質(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ù)組項(xiàng),則往對(duì)應(yīng)的回調(diào)列表中添加切換 state 狀態(tài)的方法,將 state 更改為對(duì)應(yīng)方法觸發(fā)后的狀態(tài)。

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

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

這幾個(gè)方法,存入在 deferred 對(duì)象中,并沒有存入 promise 對(duì)象。

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

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

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

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

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

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

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

返回 promise 對(duì)象,如果 obj 有傳遞,則將 promise 上的方法擴(kuò)展到 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 方法接收三個(gè)參數(shù),分別為成功的回調(diào)、失敗的回調(diào)和進(jìn)度的回調(diào)。

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

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

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

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

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

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

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

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

執(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)
    }
  })
})

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

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

deferred[tuple[1]] 是對(duì)應(yīng)的是 done、failprogress 。所以在 then 里,會(huì)依次執(zhí)行這三個(gè)方法。

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

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

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

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

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

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

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

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

從上面的分析中,可以看到,deferred 對(duì)象上并沒有done 、 failprogress 方法,這是從 promise 上擴(kuò)展來的。

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

promise 對(duì)象上沒有狀態(tài)切換方法,所以在 then 中,要綁定上下文的時(shí)候時(shí)候,綁定的都是 promise 對(duì)象,這是為了避免在執(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 方法用來管理一系列的異步隊(duì)列,如果所有的異步隊(duì)列都執(zhí)行成功,則執(zhí)行成功方法,如果有一個(gè)異步執(zhí)行失敗,則執(zhí)行失敗方法。這個(gè)方法也可以傳入非異步方法。

一些變量
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:所有的異步對(duì)象,用 slice 轉(zhuǎn)換成數(shù)組形式。

len: 異步對(duì)象的個(gè)數(shù)。

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

i: 當(dāng)前異步對(duì)象執(zhí)行的索引值。

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

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

progressContexts: 進(jìn)度回調(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 方法,在每個(gè)異步對(duì)象執(zhí)行 resolve 方法和 progress 方法時(shí)都調(diào)用。

參數(shù) i 為異步對(duì)象的索引值,參數(shù) ctx 為對(duì)應(yīng)的上下文數(shù)組,即 resolveContextsresolveContexts , val 為對(duì)應(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 方法。

依次處理異步對(duì)象
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ù)組長(zhǎng)度為異步對(duì)象的長(zhǎng)度。

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

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

else {
  --remain
}

如果不是 promise 對(duì)象,則將 remian 減少 1

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

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

最后返回的是 promise 對(duì)象。

系列文章

讀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對(duì)象詳解

License

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

作者:對(duì)角另一面

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

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/84481.html

相關(guān)文章

  • Zepto源碼Ajax模塊

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

    Crazy_Coder 評(píng)論0 收藏0
  • Zepto源碼Stack模塊

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

    crossea 評(píng)論0 收藏0
  • Zepto源碼assets模塊

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

    thursday 評(píng)論0 收藏0
  • Zepto源碼Gesture模塊

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

    coolpail 評(píng)論0 收藏0
  • Zepto源碼Form模塊

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

    陳江龍 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<