Promise原理與實現探究的一種思路

寫在前面


這個文章,展示的是一個實現Promise的思路,以及如何發現和處理問題的情境。git

從現有的Promise分析


若是咱們想要本身實現一個簡單的Promise,那現有規範規定的Promise確定是咱們最好的參照。es6

咱們先看下Promise怎麼使用:github

var promise1 = new Promise(function(resolve, reject){
      // 成功後的TODO
      resolve(value);
      // 失敗後的TODO
      reject(err);
})

來看下返回的promise1是什麼,以及它的結構是怎麼樣的:數組

image

再進行一些具體操做promise

var promise1 = new Promise(function(resolve, reject) {
    resolve('zqz')
})

promise1.then(function(result) {
   console.log(result) 
}).catch(function(err){
   console.log(err) 
})

// => 'zqz'

11

var promise1 = new Promise(function(resolve, reject) {
    reject('出現異常')
})
promise1.then(function(result) {
   console.log(result) 
}).catch(function(err){
   console.log(err) 
})

// => '出現異常'

22

從Promise的 使用方式上 和 實例 能夠看到哪些東西:框架

  • Promise是一個構造函數
  • Promise包含一個參數,這個參數類型是一個_匿名函數_
  • 匿名函數包括2個形參,分別是 rejectresolve
  • 這兩個形參類型是 函數 ,且 rejectresolve 都有一個參數, 參數類型不限定
  • 實例 是個 Promise
  • 實例的 原型 上掛載了 2個方法,分別是 thencatch,同時then能夠有多個,因此須要一個回掉函數隊列
  • 實例上 有2個屬性,分別是 PromiseStatusPromiseValue
  • Promise根據定義 PromiseStatus 須要有 3種狀態,分別是 pending, fulfilledrejected

根據上面的分析狀況,咱們先簡單的來構造一個雛形。異步

function Promise(fn) {
  this.PromiseStatus = 'pending';
  this.PromiseValue = '';

  this.resolvedCb = [];
  this.rejectedCb = [];

  var self = this;

  var resolve = function (result) {
    // 判斷狀態
     if (self.PromiseStatus === 'pending') {
      self.PromiseStatus = 'resolved';
      self.PromiseValue = result;
       // resolvedCb 隊列依次執行
       for (var i = 0;i < self.resolvedCb.length; i++) {
         self.resolvedCb[i](result)
       }
     }
   }
 
   var reject = function (err) {
     // 判斷狀態
     if (self.PromiseStatus === 'pending') {
        self.PromiseStatus = 'rejected';
        self.PromiseValue = err;
       
        // rejectedCb 隊列依次執行
       for (var i = 0;i < self.rejectedCb.length; i++) {
         self.rejectedCb[i](result)
       }
     }
   }
 
 // 錯誤處理 -> rejected
  try {
    fn(resolve, reject)
  } catch(e) {
    reject(e)
  }
  
}

固然這還不夠,由於重要的兩個功能thencatch尚未實現。async

從現有的 then 分析


分析下then的使用函數

promise1.then(function(value){
   // todo
   return value;
})
.then(function(value1){
    // todo
    return value1;
})
.then(function(value2){
  // todo
  return value2;
})
  • promise1 返回的值 須要塞到第一個then中函數的value上
  • 鏈式調用,屢次調用
  • then返回的是一個新的Promise
  • then能夠接受2個函數做爲參數,一個是成功函數,一個是失敗函數
  • return 的值 直接做爲下個 then 中匿名函數的入參

根據Promise返回的實例,咱們可看出來then是掛載在 Promise原型鏈上。測試

咱們先實現一個大致的框架:

Promise.prototype.then = function (handleSuccess, handleFail) {
    var self = this;
    var PromiseStatus = this.PromiseStatus;

    if(typeof handleSuccess === 'function') {
      handleSuccess = handleSuccess;
    } else {
      handleSuccess = function (result) {}
    }

    if(typeof handleFail === 'function') {
      handleFail = handleFail;
    } else {
      handleFail = function (err) {}
    }

    if(PromiseStatus === 'pending') {
        return new Promise(function(resolve, reject) {
          self.resolvedCb.push(handleSuccess);
          self.rejectedCb.push(handleFail);
        })
    }

    if(PromiseStatus === 'resolved') {
        return new Promise(function(resolve, reject) {
          var result = handleSuccess(self.PromiseValue);
          resolve(result);
        })
    }

    if(PromiseStatus === 'rejected') {
      return new Promise(function(resolve, reject) {
        var result = handleFail(self.PromiseValue);
        reject(result);
      })
    }
    
}

咱們先用一下,看下是否符合指望

方式一(無異步操做):

function promise1() {
  return new Promise(function(resolve, reject){
      console.log('執行promise1')
      resolve('zqz');
  })
}

promise1().then(function(result){
  console.log('執行1', 'result:'+result)
  return result + '11';
})
.then(function(result){
    console.log('執行2', 'result:'+result)
  return result + '22';
})
// => 執行promise1
// => 執行1 result:zqz
// => 執行2 result:zqz11
// => Promise {PromiseStatus: "resolved", PromiseValue: "zqz1122", resolvedCb: Array(0), rejectedCb: Array(0)}

這樣使用沒有問題!

方式二(有異步操做):

function promise1() {
  return new Promise(function(resolve, reject){
    // 異步操做
    setTimeout(function(){
      console.log('執行promise1')
      resolve('zqz');
    },1000)

  })
}

promise1().then(function(result){
  console.log('執行1', 'result:'+result)
  return result + '11';
})
.then(function(result){
    console.log('執行2', 'result:'+result)
  return result + '22';
})
// => 執行promise1
// => 執行1 result:zqz

一旦出現異步操做,就有問題!很明顯,Promise的主要做用就是控制異步操做的執行順序。

確定是哪裏有問題,咱們來分析一下,異步的時候 有哪些 不一樣

  • 當有異步操做(xhr,setTimeout等)的時候,這時候PromiseStatuspending狀態

在來看下咱們在pending時候的處理

...
// 異步時
if(PromiseStatus === 'pending') {
        return new Promise(function(resolve, reject) {
         // 這裏只是將函數塞入隊列,而後就沒有而後來。。。這是有問題的
          self.resolvedCb.push(handleSuccess);
          self.rejectedCb.push(handleFail);
        })
    }
...

這時候咱們的兩個數組:resolvedCbrejectedCb就發揮做用了,因爲咱們不知道異步何時結束,可是咱們能夠根據他們定義的前後順序注入到 隊列 中,而後根據 順序 依次執行,這樣也就保證了異步操做的執行順序。

if(PromiseStatus === 'pending') {
        return new Promise(function(resolve, reject) {
         // 一個個的塞入隊列
          self.resolvedCb.push(function(result) {
              var res = handleSuccess(self.PromiseValue);
              resolve(res);
          })
          self.rejectedCb.push(function(err) {
              var er = handleFail(self.PromiseValue);
              reject(er);
          })
        })
    }

這時候咱們用多個異步操做來測試一下

function async1() {
  return new Promise(function(resolve, reject){
    // 異步操做
    setTimeout(function(){
      console.log('執行async1')
      resolve('zqz1');
    },3000)
  })
}
function async2() {
  return new Promise(function(resolve, reject){
    // 異步操做
    setTimeout(function(){
      console.log('執行async2')
      resolve('zqz2');
    },1000)
  })
}
function async3() {
  return new Promise(function(resolve, reject){
    // 異步操做
    setTimeout(function(){
      console.log('執行async3')
      resolve('zqz3');
    },2000)
  })
}

// return 一個新的promise
async1().then(function(result){
   console.log('result = ' + result)
   return async2();
}).then(function(result){
   console.log('result = ' + result)
   return async3();
}).then(function(result){
   console.log('result = ' + result)
   return result;
})

// => Promise {PromiseStatus: "pending", PromiseValue: "", resolvedCb: Array(0), rejectedCb: Array(0)}
// => 執行async1
// => result = zqz1
// => result = [object Object]
// => result = [object Object]
// => 執行async2
// => 執行async3

這裏有兩個問題:

  • 返回promise的時候,執行順序有問題
  • 返回promise的時候,沒法進行值的傳遞

咱們再來分析下,着重看下下面這塊代碼

...
if(PromiseStatus === 'pending') {
        return new Promise(function(resolve, reject) {
          self.resolvedCb.push(function(result) {
              // 這裏返回的res有多是promise,可是咱們沒有作處理
              var res = handleSuccess(self.PromiseValue);
              resolve(res);
          })
          self.rejectedCb.push(function(err) {
              // 這裏返回的res有多是promise,可是咱們沒有作處理
              var er = handleFail(self.PromiseValue);
              reject(er);
          })
        })
    }
...

由於咱們返回的是Promise,因爲咱們沒有作處理,致使沒法正確的獲取到值。

...
if(PromiseStatus === 'pending') {
        return new Promise(function(resolve, reject) {
          self.resolvedCb.push(function(result) {
              var res = handleSuccess(self.PromiseValue);
              if (res instanceof Promise) {
                   res.then(resolve, reject);
              } else {
                   resolve(res);
              } 
          })
          self.rejectedCb.push(function(err) {
              var er = handleFail(self.PromiseValue);
              if (er instanceof Promise) {
                   er.then(resolve, reject);
              } else {
                   reject(er);
              }
          })
        })
    }
...

若是返回的是一個Promise,就繼續塞入到then裏面。

在執行一下:

async1().then(function(result){
   console.log('result = ' + result)
   return async2();
}).then(function(result){
   console.log('result = ' + result)
   return async3();
}).then(function(result){
   console.log('result = ' + result)
   return result;
})

// => Promise {PromiseStatus: "pending", PromiseValue: "", resolvedCb: Array(0), rejectedCb: Array(0)}
// => 執行async1
// => result = zqz1
// => 執行async2
// => result = zqz2
// => 執行async3
// => result = zqz3

最後一個簡單完整的 then:

Promise.prototype.then = function (handleSuccess, handleFail) {
    var self = this;
    var PromiseStatus = this.PromiseStatus;

    if(typeof handleSuccess === 'function') {
      handleSuccess = handleSuccess;
    } else {
      handleSuccess = function (result) {}
    }

    if(typeof handleFail === 'function') {
      handleFail = handleFail;
    } else {
      handleFail = function (err) {}
    }

    if(PromiseStatus === 'pending') {
        return new Promise(function(resolve, reject) {
          self.resolvedCb.push(function(result) {
              var res = handleSuccess(self.PromiseValue);
              if (res instanceof Promise) {
                   res.then(resolve, reject);
              } else {
                  resolve(er);
              } 
          })
          self.rejectedCb.push(function(err) {
              var er = handleFail(self.PromiseValue);
              if (er instanceof Promise) {
                   er.then(resolve, reject);
              } else {
                  reject(er);
              } 
          })
        })
    }
    if(PromiseStatus === 'resolved') {
        return new Promise(function(resolve, reject) {
          var result = handleSuccess(self.PromiseValue);
          resolve(result);
        })
    }
    if(PromiseStatus === 'rejected') {
      return new Promise(function(resolve, reject) {
        var result = handleFail(self.PromiseValue);
        reject(result);
      })
    }
    
}

參考


相關文章
相關標籤/搜索