1. 程式人生 > >實現自己的Promise(1)

實現自己的Promise(1)

一直以來都對Promise的實現有濃厚的興趣、感覺很好玩。很想搞清楚他的原理是什麼,而最好的辦法莫過於寫一個自己的Promise了。 首先我們需要看一下Promise的基本使用方式是什麼樣子的。

let testPromise =new Promise((resolve,reject)=>{
  setTimeout(()=>{
    resolve('ok')
  },2000)
})
testPromise.then((res)=>{
  console.log(res);
})

可以觀測到,在2s後,將會在控制檯打印出來ok。 首先,我們要確定的最關鍵的一點是,Promise沒有解決回撥函式的問題,可以看到我們的執行方案依舊是根據回撥函式來解決的,但是我們的方案更加的優雅一點、其次、如果我們註釋後面的程式碼執行以下程式碼

let testPromise =new Promise((resolve,reject)=>{
  console.log('promise');
  setTimeout(()=>{
    console.log('ok');
    resolve('ok')
  },2000)
})
// testPromise.then((res)=>{
//   console.log(res);
// })

會很快的打印出來 promise 在過了2s後打印出來OK。可見。Promise是在new的時候就執行了,而不是在then的時候。 同時我們觀測我們的程式碼可以有以下的結論

  1. Promise是一個類,需要使用new建立
  2. Promise在new的時候接收一個引數,該引數是一個函式executor
  3. executor接收兩個引數,這兩個引數也是一個函式,分別為resolve跟reject
  4. Promise生成的物件裡面有一個屬性是then。它接收兩個回撥函式onFulfilled跟onRejected
  5. 如果存在非同步的情況,那麼then中回撥函式不會立即執行、可見必有快取機制
  6. 在resolve結束之後執行onFulfilled,在reject函式之後執行onRejected,並且只會執行其中一個函式

那麼我們按照這個思路來寫一個Promise吧 1.

class MyPromise{
 
}

MyPromise應該是一個類 2.

class MyPromise{
  constructor(exector){
    exector(resolve,reject)
  }
}

可是我們的resolve跟reject在哪裡呢? 很明顯無法接受外部的引數,那麼就只能是我們MyPromise內部的函數了 3.

class MyPromise{
  constructor(exector){
    exector(resolve,reject)
  }
  resolve(){

  }
  reject(){
    
  }
}
class MyPromise{
  constructor(exector){
    exector(resolve,reject)
  }
  resolve(){

  }
  reject(){

  }
  then(onFulfilled,onRejected){

  }
}
class MyPromise{
  constructor(exector){
    this.onFulfilledCallbacks=[]
    this.onRejectedCallbacks=[]
    exector(resolve,reject)
  }
  resolve(){

  }
  reject(){

  }
  then(onFulfilled,onRejected){
    this.onFulfilledCallbacks.push(onFulfilled)
    this.onRejectedCallbacks.push(onRejected)
  }
}

在resolve之後執行onFulfilled 在這裡我們就需要引入狀態機制了。我們知道Promise是有狀態的,而resolve其實就是把Promise的狀態從pedding變成fulfilled而reject則是變成rejected

class MyPromise{
  constructor(exector){
    this.onFulfilledCallbacks=[]
    this.onRejectedCallbacks=[]
    this.state='pedding'
    this.value=null //成功的回撥值
    this.reason=null //成功的回撥值

    exector(resolve,reject)
  }
  resolve(value){
    if(this.state==='pedding'){
      this.state='fulfilled'
      this.value=value
      this.onFulfilledCallbacks.forEach(fn=>fn(value))
    }
  }
  reject(reason){
    if(this.value==='pedding'){
      this.state='rejected'
      this.reason=reason
      this.onRejectedCallbacks.forEach(fn=>fn(reason))
    }
  }
  then(onFulfilled,onRejected){
    this.onFulfilledCallbacks.push(onFulfilled)
    this.onRejectedCallbacks.push(onRejected)
  }
}

我們可以測試一下

let testPromise =new MyPromise((resolve,reject)=>{
  setTimeout(()=>{
    resolve('ok')
  },2000)
})
testPromise.then((res)=>{
  console.log('then');
  console.log(res);
})

可以發現一些問題因為執行環境的問題,所以this的指向是有問題的,我們可以手動繫結一下


class MyPromise{
  constructor(exector){
    this.onFulfilledCallbacks=[]
    this.onRejectedCallbacks=[]
    this.state='pedding'
    this.value=null //成功的回撥值
    this.reason=null //成功的回撥值

    exector(this.resolve.bind(this),this.reject.bind(this))
  }
  resolve(value){
    if(this.state==='pedding'){
      this.state='fulfilled'
      this.value=value
      this.onFulfilledCallbacks.forEach(fn=>fn(value))
    }
  }
  reject(reason){
    if(this.state==='pedding'){
      this.state='rejected'
      this.reason=reason
      this.onRejectedCallbacks.forEach(fn=>fn(reason))
    }
  }
  then(onFulfilled,onRejected){
    this.onFulfilledCallbacks.push(onFulfilled)
    this.onRejectedCallbacks.push(onRejected)
  }
}

這樣實際上執行結果就符合我們的預期了 但是其實還是有些問題的

  1. 不是很符合PromiseA+的規範
  2. reject跟resolve函式其實不應該被返回
  3. 是否在一開始的時候就存在fulfilled狀態呢?

下面給出一版本更規範一點的實現

class MyPromise{
  constructor(exector){
    this.onFulfilledCallbacks=[]
    this.onRejectedCallbacks=[]
    this.state='pedding'
    this.value=null //成功的回撥值
    this.reason=null //成功的回撥值
    let resolve=(value)=>{
      if(this.state==='pedding'){
        this.state='fulfilled'
        this.value=value
        this.onFulfilledCallbacks.forEach(fn=>fn(value))
      }
    }
    let reject=(reason)=>{
      if(this.state==='pedding'){
        this.state='rejected'
        this.reason=reason
        this.onRejectedCallbacks.forEach(fn=>fn(reason))
      }
    }

    exector(resolve,reject)
  }
  then(onFulfilled,onRejected){
    if(this.state==='fulfilled'){
      onFulfilled(this.value)
    }
    if(this.state==='rejected'){
      onRejected(this.reason)
    }
    if(this.state==='pedding'){
      this.onFulfilledCallbacks.push(onFulfilled)
      this.onRejectedCallbacks.push(onRejected)
    }
  }
}
let testPromise =new MyPromise((resolve,reject)=>{
  setTimeout(()=>{
    resolve('ok')
  },2000)
})
testPromise.then((res)=>{
  console.log('then');
  console.log(res);
})

關於PromiseA+規範我們可以參考

這樣我們就實現了一個基本具有Promise樣子的MyPromise。但是。回到開頭我們提到了Promise依舊是依靠回撥函式來執行的,這個樣子跟我們使用回撥函式的區別不是很大,為什麼還要寫Promise呢?原因在於Promise的精髓。鏈式呼叫 也就是

let testPromise =new Promise((resolve,reject)=>{
  setTimeout(()=>{
    resolve('ok')
  },2000)
})
testPromise.then((res)=>{
  console.log(res);
  return new Promise((resolve,reject)=>{
    setTimeout(()=>{
      resolve('ok2')
    },2000)
  })
}).then((res)=>{
  console.log(res);
})

下一篇文章我們將會研究一下Promise是怎麼實現鏈式呼叫的