用es5實現es6的promise,完全搞懂promise的原理

1.promise的含義

Promise 是異步編程的一種解決方案,比傳統的解決方案——回調函數和事件——更合理和更強大。它由社區最先提出和實現,ES6 將其寫進了語言標準,統一了用法,原生提供了Promise對象。git

所謂Promise,簡單說就是一個容器,裏面保存着某個將來纔會結束的事件(一般是一個異步操做)的結果。從語法上說,Promise 是一個對象,從它能夠獲取異步操做的消息。Promise 提供統一的 API,各類異步操做均可以用一樣的方法進行處理。github

Promise對象有如下兩個特色。編程

(1)對象的狀態不受外界影響。Promise對象表明一個異步操做,有三種狀態:pending(進行中)、fulfilled(已成功)和rejected(已失敗)。只有異步操做的結果,能夠決定當前是哪種狀態,任何其餘操做都沒法改變這個狀態。這也是Promise這個名字的由來,它的英語意思就是「承諾」,表示其餘手段沒法改變。數組

(2)一旦狀態改變,就不會再變,任什麼時候候均可以獲得這個結果。Promise對象的狀態改變,只有兩種可能:從pending變爲fulfilled和從pending變爲rejected。只要這兩種狀況發生,狀態就凝固了,不會再變了,會一直保持這個結果,這時就稱爲 resolved(已定型)。若是改變已經發生了,你再對Promise對象添加回調函數,也會當即獲得這個結果。這與事件(Event)徹底不一樣,事件的特色是,若是你錯過了它,再去監聽,是得不到結果的。promise

有了Promise對象,就能夠將異步操做以同步操做的流程表達出來,避免了層層嵌套的回調函數。此外,Promise對象提供統一的接口,使得控制異步操做更加容易。bash

Promise也有一些缺點。首先,沒法取消Promise,一旦新建它就會當即執行,沒法中途取消。其次,若是不設置回調函數,Promise內部拋出的錯誤,不會反應到外部。第三,當處於pending狀態時,沒法得知目前進展到哪個階段(剛剛開始仍是即將完成)。框架

2.小試promise用法:

Promise構造函數接受一個函數做爲參數,該函數的兩個參數分別是resolvereject。它們是兩個函數,由 JavaScript 引擎提供,不用本身部署。異步

resolve函數的做用是,將Promise對象的狀態從「未完成」變爲「成功」(即從 pending 變爲 resolved),在異步操做成功時調用,並將異步操做的結果,做爲參數傳遞出去;reject函數的做用是,將Promise對象的狀態從「未完成」變爲「失敗」(即從 pending 變爲 rejected),在異步操做失敗時調用,並將異步操做報出的錯誤,做爲參數傳遞出去。異步編程


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);
});複製代碼

從demo中能夠看到,在ES6中Promise對象是一個構造函數,用來生成Promise實例。而且Promise實例生成之後,能夠用then方法分別指定resolved狀態和rejected狀態的回調函數。函數

3.手寫一個promise,完全掌握它的原理

首先,咱們先搭建好代碼的骨架:

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

  function resolve(value){

  }
  function reject(error){

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

}複製代碼

咱們先建立一個Promise構造函數,並傳入一個回調函數callback,callback裏面傳入兩個函數做爲參數,一個是resove,一個是reject。並在Promise的原型上加入咱們的then方法。框架搭好了,接下來咱們來一點點的完善框架裏面的內容,能夠這麼說,把resolve,reject和then補充完,基本能夠說就是把Promise完成了。

而後緊接着,咱們完善一下resolve和reject:

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)
        }
    }
  }
}複製代碼

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

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

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

promise.then(onFulfilled, onRejected)複製代碼

onFulfilledonRejected 都是可選參數。

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

onFulfilled 特性

若是 onFulfilled 是函數:

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

onRejected 特性

若是 onRejected 是函數:

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

調用時機

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

調用要求

onFulfilledonRejected 必須被做爲函數調用(即沒有 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)
}複製代碼

最後附上完整的代碼:

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)
}複製代碼
相關文章
相關標籤/搜索