【原】手寫一個promise 手把手教你實現一個完整的 Promise

上一篇文章中,咱們介紹了Promise的基本使用,在這篇文章中,咱們試着本身來寫一個Promise,主要是學習Promise的內部機制,學習它的編程思想。javascript

!!!備註:本文寫的很差,僅供本身學習之用,具體的實現過程建議看下面的參考文章。因此本文沒有發佈到博客園首頁和其餘地方html

 

Promise API分析

正常使用方法

咱們來看一個正常的使用:java

var p=new Promise(function(resolve,rejcet){
    setTimeout(function(){
        if(true){
            resolve('success');
        }else{
            rejcet('failure');
        }
    },1000);
});
p.then(function(value){
    console.log(value);
},function(error){
    console.log(error);
});
//success

接下來咱們就來實現這麼一個Promise.git

 

先來了解相關的一些術語: es6

    解決(fulfill):指一個 promise 成功時進行的一系列操做,如狀態的改變、回調的執行。雖然規範中用 fulfill 來表示解決,但在後世的 promise 實現多以 resolve 來指代之。
    拒絕(reject):指一個 promise 失敗時進行的一系列操做。
    終值(eventual value):所謂終值,指的是 promise 被解決時傳遞給解決回調的值,因爲 promise 有一次性的特徵,所以當這個值被傳遞時,標誌着 promise 等待態的結束,故稱之終值,有時也直接簡稱爲值(value)。
    據因(reason):也就是拒絕緣由,指在 promise 被拒絕時傳遞給拒絕回調的值。github

 

Promise的流程圖分析

promise的執行流程如以下:編程

Promise鏈式操做中,執行順序是如何保證的

    每一個promise後面鏈一個對象該對象包含onfulfiled,onrejected,子promise三個屬性,當父promise 狀態改變完畢,執行完相應的onfulfiled/onfulfiled的時候呢,拿到子promise,在等待這個子promise狀態改變,再執行相應的onfulfiled/onfulfiled。依次循環直到當前promise沒有子promisesegmentfault

 

如何讓異步的value在thenable函數中拿到

    將resolve/reject函數和onfulfiled/onrejected放入同一個對象(promise對象)裏面,resolve/reject的時候將value設置this.value=xxx。onfulfiled/onrejected執行的時候呢,onfulfiled(this.value)便可。數組

   在這裏避免頭暈,解釋一下,onfulfilled和onrejected指的是then裏面的兩個函數。promise

 

狀態機制切換

     如圖所示,狀態只能由pengding-->fulfilled,或者由pending-->rejected這樣轉變。

     只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果。就算改變已經發生了,你再對Promise對象添加回調函數,也會當即獲得這個結果。這與事件(Event)徹底不一樣,事件的特色是,若是你錯過了它,再去監聽,是得不到結果的。

 

開始手寫Promise

一、 首先咱們來寫好咱們的框架

ES6原生的構建方式爲:

// Promise構造函數接收一個executor函數,executor函數執行完同步或異步操做後,調用它的兩個參數resolve和reject
var promise = new Promise(function(resolve, reject) {
  /*
    若是操做成功,調用resolve並傳入value
    若是操做失敗,調用reject並傳入reason
  */
})

咱們就按照這種方式來搭好框架

function Promise(callback) {
  var self = this
  self.status = 'PENDING' // Promise當前的狀態
  self.data = undefined  // Promise的值
  self.onResolvedCallback = [] // Promise resolve時的回調函數集
  self.onRejectedCallback = [] // Promise reject時的回調函數集
  callback(resolve, reject) // 執行executor並傳入相應的參數

  function resolve(value){

  }
  function rejecte(error){

  }
}
// 添加咱們的then方法
Promise.prototype.then=function(){

}

咱們構造一個Promise函數,並傳入一個回調callback,callback裏面傳入兩個函數做爲參數,一個是resove,一個是reject。並在Promise的原型上加入咱們的then方法。

 

二、完善框架裏面的內容 

框架搭好了,接下來咱們來一點點的完善框架裏面的內容,能夠這麼說,把resolve,reject和then補充完,基本能夠說就是把Promise完成了。

咱們先來完善咱們的resolve和rejected:

function Promise(callback) {
  var self = this
  self.status = 'PENDING' // Promise當前的狀態
  self.data = undefined  // Promise的值
  self.onResolvedCallback = [] // Promise resolve時的回調函數集
  self.onRejectedCallback = [] // Promise reject時的回調函數集
  callback(resolve, reject) // 執行executor並傳入相應的參數

  function resolve(value){
      if(self.status=='PENDING'){
          self.status=='FULFILLED';
          self.data=value;
          // 依次執行成功以後的函數棧
          for(var i = 0; i < self.onResolvedCallback.length; i++) {
            self.onResolvedCallback[i](value)
          }
      }
  }

  function rejecte(error){
    if (self.status === 'PENDING') {
       self.status = 'REJECTED'
       self.data = error;
       // 依次執行失敗以後的函數棧
       for(var i = 0; i < self.onRejectedCallback.length; i++) {
           self.onRejectedCallback[i](error)
        }
    }
  }
}

若是是penging,則改變相應的狀態,並把resolve和reject的值保存子data裏面。

 

接下來咱們實現咱們的then方法:

then方法是Promise的核心,所以這裏會花比較大的篇幅去介紹then:

 一個promise的then接受兩個參數:

promise.then(onFulfilled, onRejected)

onFulfilled 和 onRejected 都是可選參數。

  • 若是 onFulfilled 不是函數,其必須被忽略
  • 若是 onRejected 不是函數,其必須被忽略

onFulfilled 特性

若是 onFulfilled 是函數:

  • 當 promise 執行結束後其必須被調用,其第一個參數爲 promise 的終值,也就是resolve傳過來的值
  • 在 promise 執行結束前其不可被調用
  • 其調用次數不可超過一次

onRejected 特性

若是 onRejected 是函數:

  • 當 promise 被拒絕執行後其必須被調用,其第一個參數爲 promise 的據因,也就是reject傳過來的值
  • 在 promise 被拒絕執行前其不可被調用
  • 其調用次數不可超過一次

調用時機

onFulfilled 和 onRejected 只有在執行環境堆棧僅包含平臺代碼時纔可被調用(平臺代碼指引擎、環境以及 promise 的實施代碼)

調用要求

onFulfilled 和 onRejected 必須被做爲函數調用(即沒有 this 值,在 嚴格模式(strict) 中,函數 this 的值爲 undefined ;在非嚴格模式中其爲全局對象。)

屢次調用

then 方法能夠被同一個 promise 調用屢次

  • 當 promise 成功執行時,全部 onFulfilled 需按照其註冊順序依次回調
  • 當 promise 被拒絕執行時,全部的 onRejected 需按照其註冊順序依次回調

返回

then 方法必須返回一個 promise 對象 

promise2 = promise1.then(onFulfilled, onRejected);
  • 若是 onFulfilled 或者 onRejected 返回一個值 x ,則運行下面的 Promise 解決過程[[Resolve]](promise2, x)
  • 若是 onFulfilled 或者 onRejected 拋出一個異常 e ,則 promise2 必須拒絕執行,並返回拒因 e
  • 若是 onFulfilled 不是函數且 promise1 成功執行, promise2 必須成功執行並返回相同的值
  • 若是 onRejected 不是函數且 promise1 拒絕執行, promise2 必須拒絕執行並返回相同的據因

不論 promise1 被 reject 仍是被 resolve 時 promise2 都會被 resolve,只有出現異常時纔會被 rejected

   每一個Promise對象均可以在其上屢次調用then方法,而每次調用then返回的Promise的狀態取決於那一次調用then時傳入參數的返回值,因此then不能返回this,由於then每次返回的Promise的結果都有可能不一樣。

 

接下來咱們來寫咱們的then方法:

Promise.prototype.then = function(onResolved, onRejected) {
  var self = this
  var promise2

  // 根據標準,若是then的參數不是function,則咱們須要忽略它,此處以以下方式處理
  onResolved = typeof onResolved === 'function' ? onResolved : function(value) {}
  onRejected = typeof onRejected === 'function' ? onRejected : function(reason) {}

  if (self.status === 'resolved') {
    // 若是promise1(此處即爲this/self)的狀態已經肯定而且是resolved,咱們調用onResolved
    // 由於考慮到有可能throw,因此咱們將其包在try/catch塊裏
    return promise2 = new Promise(function(resolve, reject) {
      try {
        var x = onResolved(self.data)
        if (x instanceof Promise) { // 若是onResolved的返回值是一個Promise對象,直接取它的結果作爲promise2的結果
          x.then(resolve, reject)
        }
        resolve(x) // 不然,以它的返回值作爲promise2的結果
      } catch (e) {
        reject(e) // 若是出錯,以捕獲到的錯誤作爲promise2的結果
      }
    })
  }

  // 此處與前一個if塊的邏輯幾乎相同,區別在於所調用的是onRejected函數,就再也不作過多解釋
  if (self.status === 'rejected') {
    return promise2 = new Promise(function(resolve, reject) {
      try {
        var x = onRejected(self.data)
        if (x instanceof Promise) {
          x.then(resolve, reject)
        }
      } catch (e) {
        reject(e)
      }
    })
  }

  if (self.status === 'pending') {
  // 若是當前的Promise還處於pending狀態,咱們並不能肯定調用onResolved仍是onRejected,
  // 只能等到Promise的狀態肯定後,才能確實如何處理。
  // 因此咱們須要把咱們的**兩種狀況**的處理邏輯作爲callback放入promise1(此處即this/self)的回調數組裏
  // 邏輯自己跟第一個if塊內的幾乎一致,此處不作過多解釋
    return promise2 = new Promise(function(resolve, reject) {
      self.onResolvedCallback.push(function(value) {
        try {
          var x = onResolved(self.data)
          if (x instanceof Promise) {
            x.then(resolve, reject)
          }
        } catch (e) {
          reject(e)
        }
      })

      self.onRejectedCallback.push(function(reason) {
        try {
          var x = onRejected(self.data)
          if (x instanceof Promise) {
            x.then(resolve, reject)
          }
        } catch (e) {
          reject(e)
        }
      })
    })
  }
}

// 爲了下文方便,咱們順便實現一個catch方法
Promise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected)
}

 

完整代碼以下:

try {
  module.exports = Promise
} catch (e) {}
  
function Promise(executor) {
  var self = this
  
  self.status = 'pending'
  self.onResolvedCallback = []
  self.onRejectedCallback = []
  
  function resolve(value) {
    if (value instanceof Promise) {
      return value.then(resolve, reject)
    }
    setTimeout(function() { // 異步執行全部的回調函數
      if (self.status === 'pending') {
        self.status = 'resolved'
        self.data = value
        for (var i = 0; i < self.onResolvedCallback.length; i++) {
          self.onResolvedCallback[i](value)
        }
      }
    })
  }
  
  function reject(reason) {
    setTimeout(function() { // 異步執行全部的回調函數
      if (self.status === 'pending') {
        self.status = 'rejected'
        self.data = reason
        for (var i = 0; i < self.onRejectedCallback.length; i++) {
          self.onRejectedCallback[i](reason)
        }
      }
    })
  }
  
  try {
    executor(resolve, reject)
  } catch (reason) {
    reject(reason)
  }
}
  
function resolvePromise(promise2, x, resolve, reject) {
  var then
  var thenCalledOrThrow = false
  
  if (promise2 === x) {
    return reject(new TypeError('Chaining cycle detected for promise!'))
  }
  
  if (x instanceof Promise) {
    if (x.status === 'pending') { //because x could resolved by a Promise Object
      x.then(function(v) {
        resolvePromise(promise2, v, resolve, reject)
      }, reject)
    } else { //but if it is resolved, it will never resolved by a Promise Object but a static value;
      x.then(resolve, reject)
    }
    return
  }
  
  if ((x !== null) && ((typeof x === 'object') || (typeof x === 'function'))) {
    try {
      then = x.then //because x.then could be a getter
      if (typeof then === 'function') {
        then.call(x, function rs(y) {
          if (thenCalledOrThrow) return
          thenCalledOrThrow = true
          return resolvePromise(promise2, y, resolve, reject)
        }, function rj(r) {
          if (thenCalledOrThrow) return
          thenCalledOrThrow = true
          return reject(r)
        })
      } else {
        resolve(x)
      }
    } catch (e) {
      if (thenCalledOrThrow) return
      thenCalledOrThrow = true
      return reject(e)
    }
  } else {
    resolve(x)
  }
}
  
Promise.prototype.then = function(onResolved, onRejected) {
  var self = this
  var promise2
  onResolved = typeof onResolved === 'function' ? onResolved : function(v) {
    return v
  }
  onRejected = typeof onRejected === 'function' ? onRejected : function(r) {
    throw r
  }
  
  if (self.status === 'resolved') {
    return promise2 = new Promise(function(resolve, reject) {
      setTimeout(function() { // 異步執行onResolved
        try {
          var x = onResolved(self.data)
          resolvePromise(promise2, x, resolve, reject)
        } catch (reason) {
          reject(reason)
        }
      })
    })
  }
  
  if (self.status === 'rejected') {
    return promise2 = new Promise(function(resolve, reject) {
      setTimeout(function() { // 異步執行onRejected
        try {
          var x = onRejected(self.data)
          resolvePromise(promise2, x, resolve, reject)
        } catch (reason) {
          reject(reason)
        }
      })
    })
  }
  
  if (self.status === 'pending') {
    // 這裏之因此沒有異步執行,是由於這些函數必然會被resolve或reject調用,而resolve或reject函數裏的內容已經是異步執行,構造函數裏的定義
    return promise2 = new Promise(function(resolve, reject) {
      self.onResolvedCallback.push(function(value) {
        try {
          var x = onResolved(value)
          resolvePromise(promise2, x, resolve, reject)
        } catch (r) {
          reject(r)
        }
      })
  
      self.onRejectedCallback.push(function(reason) {
          try {
            var x = onRejected(reason)
            resolvePromise(promise2, x, resolve, reject)
          } catch (r) {
            reject(r)
          }
        })
    })
  }
}
  
Promise.prototype.catch = function(onRejected) {
  return this.then(null, onRejected)
}
  
Promise.deferred = Promise.defer = function() {
  var dfd = {}
  dfd.promise = new Promise(function(resolve, reject) {
    dfd.resolve = resolve
    dfd.reject = reject
  })
  return dfd
}
View Code

 

  

  

本文沒有寫好,有點頭暈,因此具體的實現過程仍是建議看下面的參考文章。 

 

參考:

大白話promise

史上最易讀懂的 Promise 實現

談談 ES6 的 Promise 對象

手把手教你實現一個完整的 Promise

Promise簡單實現(正常思路版)

promise源碼分析

【翻譯】Promises/A+規範

相關文章
相關標籤/搜索