我能不能在不看別人怎麼實現promise的狀況下,本身實現一個promise?

我能不能在不看別人怎麼實現promise的狀況下,本身實現一個promise?

都8102年爲何還要寫promise實現?

​ 年前和年後面試了幾家公司, 雖然都掛了… 可是都談到了一個面試題,就是promise. 雖然使用promise很簡單,並且我回答的都沒問題.javascript

​ 可是面試官都問到了一個題目. "若是讓你實現一個promise.all方法, 怎麼實現 ? " 臨時想了一個每一個promise.then裏用計數器+1, 在判斷計數器是否等於參數Array[promise]的 length 來判斷promise是否都完成的實現思路, 也不知道算不算是對的.java

​ 而後就回來想本身能不能在不看任何人的代碼的狀況下, 實現一個promise。git

第一步: 先稍微分析一下原生的Promise

Promise 使用方式

var a = new Promise(function( resolve, reject ){})github

new一個Promise實例,傳入一個函數,裏面有兩個參數。面試

resolve:成功時調用,並將成功後的數據傳進then方法裏。數組

reject:失敗的時候調用,並將失敗的數據傳進catch方法裏。promise

Promise的原型方法

Promise原型

很簡單,只有咱們常見的then catch還有finally方法。不過finally方法應該不屬於ES6標準的,因此先忽略。(上網查了一下好像是ES2018標準)緩存

第二步:開寫

2.1:構造函數

什麼都不想,先寫一個構造函數,就叫 Future 把。函數

由於Promise有兩種狀態,因此我給他加一個 statusoop

function Future(func){
  this.status = null;
}

2.2:增長resolve和reject

​ 接着須要執行傳入的函數,並傳給他一個resolvereject方法。常常用Promise的同窗應該知道 Promise.resolvePromise.reject

​ 可是沒在原型裏找到這兩個方法,因此我就直接在Future上加這兩個方法。

// 只要執行了resolve或者reject確定要改變status, 因此對實例的status作更新
Future.resolve = function (data) {
  this.status = 'resolve'
  this.data = data
}

Future.reject = function (data) {
  this.status = 'reject'
  this.data = data
}

這兩個這裏的data要在then裏用,因此仍是得緩存起來,而後將這兩個方法傳進func裏,這裏對構造函數再作改動

function Future(func){
  this.status = null;
  this.data = null;
  
  func(Future.resolve, Future.resolve)
}

​ 可是這裏有一個問題,resolve執行的時候,this並非指向當前的promise實例的,這時我就想到了bind方法。因此必須在初始化的時候把resolve和reject的做用域給綁定好。對構造函數再次作修改。( 還要加上setTimeout , 加入even loop,這個是後加的)

function Future(func){
  if(typeOf func !== "function") throw new Errow("Future 的參數必須爲函數");
  
  var _this = this;
  this.status = null;
  this.data = null;
  setTimeout(function () {
    func(Future.resolve.bind(_this), Future.resolve.bind(_this))
  })
}

2.3:實現then和catch

回顧一下then的使用方式:傳入一個函數,在promise執行resolve後,纔會調用,而且函數的參數就是調用resolve的時候傳入的值。而且能夠return一個值。給下一個then繼續調用。

​ 因此then函數應該很簡單,直接緩存這個函數,resolve的時候再拿出來調用便可。而關於鏈式調用,一開始想到的就是return this

​ 因此一開始我先是這麼寫的

function Future(func){
      //再加一個函數隊列數組和一個錯誤狀態的執行函數
      this.queue = [];
      this.failCb = null;
     ...其他代碼省略
}
Future.prototype.then = function (callback) {
      if(typeof callback !== "function") throw new Errow("then必須傳入函數");
  
    if(this.status === 'resolve'){
        this.data = callback(this.data);
    }else if(this.status === null){
        this.queue.push(callback);
   }

   return this;
}

Future.prototype.catch = function (callback) {
      if(typeof callback !== "function") throw new Errow("catch必須傳入函數");
  
    if (this.status === 'reject') {
        this.data = callback(this.data);
    }else if(this.status === null){
        this.failCb = callback;
    }
  
    return this;
}

2.4:實現resolve和reject

​ 其餘的都好了,接着就是在resolve裏去執行隊列裏的函數。reject裏執行錯誤函數。

Future.resolve = function (data) {
    var context = this;
    context.status = 'resolve';
    context.data = data;
  
      //先把第一個函數拿出來
    var func = context.queue.shift();
    if(func){
        try{
            var d = func(data);
              //函數能夠返回一個值,也能夠返回一個promise
            if(d instanceof Future){
                d = d.data;
            }
              //遞歸的方式再執行下一個,這裏再用call去改變this的指向
            Future.resolve.call(context, d);
        }catch(err){
              //捕捉報錯,執行catch
            Future.reject.call(context, err);
        }
    }
}

Future.reject = function (data) {
    this.status = 'reject';
    this.data = data;
    if(this.failCb){
        this.failCb(data)
    }else{
        throw new Error("promise catch")
    }
}

以上。

​ 到這裏呢,就是那時臨時想臨時作的初版。

後記

​ 固然,後面又大改了一些東西。最主要的是then函數不該該返回this。應該是一個新的promise。若是按照如今這麼作,通過多個then以後,初始的data就變成了最後一個值了。咱們但願的是要保留最初初始化的時候的那個值。

//好比
var a = new Future(function(resolve, reject){
    setTimeout(function(){
        console.log('success')
        resolve(true)
    }, 1000)
})

a.then(function(res){
    console.log(res);
    return "啦拉拉"
})

setTimeout(function(){
    a.then(function(res){
          //這裏就會輸出 "啦拉拉"。其實指望的是輸出 true
        console.log("settimeout: ", res)
    })
},2000)

​ 後來爲了解決這個,忽然陷入了牛角尖。。。花了一天才作完。水平有限,只能作到這樣了,最後附上完整代碼吧。

最後的版本

總結

​ 後來去看了看別人實現的方法。大致思路應該也是差很少的。其實就作個記錄總結,方便之後面試用。嘻嘻(^__^)。

相關文章
相關標籤/搜索