promise 基礎版(雛形)

function Promise(fn){
    //須要一個成功時的回調
    var self = this
    var callback;
    //一個實例的方法,用來註冊異步事件
    self.then = function(done){
      callback = done;
    }
    // resolve 比 then 先執行 此時 callback 不存在
    // 因此 加一個 setTimeout 讓resolve 函數在回調隊列的末尾
    // 爲啥是0秒? 爲啥處於回調隊列末尾?
    //(權威指南)若是以0毫秒的超時時間來調用setTimeout(),那麼指定的函數不會當即執
    // 行,相反會把它放到隊列中去,等到前面處於等待狀態的事件處理程序所有執行完成後,
    // 再「當即」調用它。
    // 如下 方式寫 提示 resolve 未定義 涉及閉包 做用域鏈的問題
    // setTimeout(function () {
    //   function resolve(value){
    //      callback && callback(value);
    //   }
    // }, 0)
    
    // 改進方式
    function resolve (value) {
      setTimeout(function () {
        callback && callback(value)
      }, 0)
    }
    fn(resolve);
  }

調用promisehtml

// 實例化promise 回調函數fn執行成功,並執行resolve函數,此時在回調隊列裏面添加一
 // 一個callback函數,並將resolve的參數傳遞出去。
 var promise = new Promise(function (resolve) {
    resolve(['3', 'aaa'])
 })
// 調用then函數,並執行then回調,將then函數的參數done回調函數賦值給callback,
// 在回調隊列裏面(以前setTimeout添加進去的回調隊列)執行then的回調函數
 promise.then(function (data) {
    console.log('data', data)
 })

可是以上方式寫,咱們永遠都只能執行then中一個回調隊列,這顯然不健壯。咱們結合js設計模式的發佈--消息訂閱模式,再結合構造函數return this 知識點,稍微改造下:設計模式

function Promise(fn){
    //須要一個成功時的回調
    var self = this
    self.deferreds = []; // then函數 回調隊列 儲存容器
    //一個實例的方法,用來註冊異步事件
    self.then = function(onFulfilled){
      self.deferreds.push(onFulfilled)
      console.log('self.deferreds', self.deferreds) 
      // 調用兩次then 回調隊列會逐個push
      return self // 鏈式調用then
    }

    // 改進方式
    function resolve (value) {
      setTimeout(function () {
        self.deferreds.forEach(function (deferred) {
           deferred && deferred(value)
        })
      }, 0)
    }
    fn(resolve);
}

調用then函數:promise

promise.then(function (data) {
    console.log('data', data)
}).then(function (resp) {
    console.log('resp', resp)
})

衆所周知,構造函數Promise存在三個互斥狀態:pending、fulfilled、rejected。Promise 對象的狀態改變,只有兩種可能:從 pending 變爲 fulfilled 和從 pending 變爲 rejected。只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果。閉包

因此:咱們改進代碼以下:異步

//略
// 初始化設置狀態
self.status = 'pending'
//略
// ...
//略
// resolve的時候 將狀態置爲
self.status = 'fulfilled'
//略

調用執行,一樣能夠獲得咱們想要的數據。
可是
可是
可是
僅僅加上上面兩行代碼是不行的,仔細理解加粗的那幾個字,再結合咱們的代碼來看。
當咱們調用then函數的時候,往咱們then回調隊列裏面push回調函數,最終無論狀態是pending
仍是fulfilled,回調隊列的函數都是被resolve函數觸發的。這樣就違背了這句話:只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果。
當咱們狀態改變爲fulfilled,咱們並無真正改變狀態,每次再從新執行的時候,咱們又從新走了一次then添加回調,而後由resolve來觸發回調的過程。
因此then函數改進代碼以下:函數

// 當status == 'pending'的時候,咱們才往then的回調隊列push回調函數。
// 不然 直接執行回調函數,不會由resolve來觸發then的回調函數執行。
if(self.status == 'pending') {
  self.deferreds.push(onFulfilled)
  return self
}
onFulfilled(value)
return self // 鏈式調用then

因此加入value後,最終代碼以下:
基本就實現了鏈式調用then的一個帶有pending 和 fulfilled 狀態的Promise
後續會加上reject(), rejected以及最難理解的串行promise。this

function Promise(fn){
    //須要一個成功時的回調
    var self = this
    self.deferreds = []; // then函數 回調隊列 儲存容器
    self.status = 'pending'
    self.value = null
    //一個實例的方法,用來註冊異步事件
    self.then = function(onFulfilled){
      if(self.status == 'pending') {
        self.deferreds.push(onFulfilled)
        return self
      }
      onFulfilled(self.value)
      return self // 鏈式調用then
    }

    // 改進方式
    function resolve (newValue) {
      setTimeout(function () {
        self.value = newValue
        self.status = 'fulfilled'
        self.deferreds.forEach(function (deferred) {
           deferred && deferred(self.value)
        })
      }, 0)
    }
    fn(resolve);
 }

附錄參考文獻

相關文章
相關標籤/搜索