按部就班實現Promise

實現promise要在熟練使用的基礎上完成,首先要明確幾點數組

  1. promise是一個類
  2. promise有三種狀態:成功(fulfilled)、失敗(rejected)、等待(pending)
    1. pending -> fulfilled / rejected
    2. 狀態一旦肯定就不能夠再改變
  3. resolve和reject函數是用來改變狀態的
    1. resolve: fulfilled
    2. reject: rejected
  4. 調用then方式傳入兩個參數,第一個參數爲狀態fulfilled時的回調函數,第二個參數爲狀態爲rejected時的回調函數

初版 - 基礎核心原理的實現

const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(exector) {
    exector(this.reslove, this.reject)
  }
  
  status = PENDING // promise狀態
  value = undefined // 成功時的值 then方法須要返回
  error = undefined // 失敗時的值 then方法須要返回
  
  // 成功時的函數
  reslove = value => {
    // 修改promise狀態
    this.status = FULFILLED
    this.value = value
  }
  // 失敗時的函數
  reject = error => {
    // 修改promise狀態
    this.status = REJECTED
    this.error = error
  }
  
  then(successCallback, failCallback) {
    switch (this.status) {
            case FULFILLED:
                successCallback(this.value)
                break
            case REJECTED:
                failCallback(this.error)
                break
                
        }
  }
}
// test
const promise = new MyPromise((reslove, reject) => {
    reslove('reslove')
})
  
promise.then(value => {
    console.log(value) // reslove
}, error => {
    console.log(error)
})
複製代碼

經過上面的代碼,咱們成功實現了對promise基礎原理。調用promise.then方法能夠成功打印出‘reslove’。可是,在then方法的實現中咱們並無對PENDING狀態作處理。也就是說,假如咱們進行異步操做,當前的promise是沒法處理的。好比:promise

const promise = new MyPromise((reslove, reject) => {
  setTimeout(()=> {
    reslove('reslove')
  },2000)
})
複製代碼

第二版 - 處理異步狀況

那麼異步狀況如何處理呢?以上面setTimeout爲例,reslove是在2秒後執行,在調用then方法2秒內當前promise的狀態仍然爲PENDING。 兩步解決:markdown

  1. PENDING狀態時,對成功or失敗回調做暫存
  2. 在reslove/reject 函數中判斷是否有回調函數,有則執行
const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(exector) {
    exector(this.reslove, this.reject)
  }
  
  status = PENDING // promise狀態
  value = undefined // 成功時的值 then方法須要返回
  error = undefined // 失敗時的值 then方法須要返回
    successCallback = undefined // 成功回調
    failCallback = undefined // 失敗回調
  // 成功時的函數
  reslove = value => {
    // 修改promise狀態
    this.status = FULFILLED
    this.value = value
    this.successCallback && this.successCallback(this.value)
  }
  // 失敗時的函數
  reject = error => {
    // 修改promise狀態
    this.status = REJECTED
    this.error = error
    this.failCallback && this.failCallback(this.error)
  }
  then(successCallback, failCallback) {
    switch (this.status) {
            case FULFILLED:
                successCallback(this.value)
                break
            case REJECTED:
                failCallBack(this.error)
                break
      // 異步狀況處理
      case PENDING:
        this.successCallback = successCallback
        this.failCallback = failCallback
                break
        }
  }
}
// test
const promise = new MyPromise((reslove, reject) => {
    setTimeout(()=> {
        reslove('reslove')
    }, 2000)    
})
  
promise.then(value => {
    console.log(value) // 2秒後: reslove
}, error => {
    console.log(error)
})
複製代碼

實現第二版後,問題又來了。咱們平時使用promise時同一個promise下的then方法是能夠屢次調用的。咱們將測試代碼改成異步

// test
const promise = new MyPromise((reslove, reject) => {
  setTimeout(()=> {
    reslove('reslove')
  }, 2000)  
})
promise.then(value => {
    console.log('第一次調用then: ', value)
})
promise.then(value => {
    console.log('第二次調用then: ', value)
})
// 第二次調用then reslove
複製代碼

發現代碼只執行了最後一次then方法,打印「第二次調用then: reslove」。這顯然是與咱們的預期不相符的函數

第三版 - 屢次調用

要實現屢次調用,咱們須要作的是: 以數組形式暫存多個callback,而且在reslove/reject的時候依次調用測試

const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
  constructor(exector) {
    exector(this.reslove, this.reject)
  }
  status = PENDING // promise狀態
  value = undefined // 成功時的值 then方法須要返回
  error = undefined // 失敗時的值 then方法須要返回
  // 數組暫存
  successCallback = [] // 成功回調
  failCallback = [] // 失敗回調
  // 成功時的函數
  reslove = value => {
    // 修改promise狀態
    this.status = FULFILLED
    this.value = value
    // 依次調用
    while(this.successCallback.length) this.successCallback.shift()(this.value)
  }
  // 失敗時的函數
  reject = error => {
    // 修改promise狀態
    this.status = REJECTED
    this.error = error
    // 依次調用
    while(this.failCallback.length) this.failCallback.shift()(this.error)
  }
  then(successCallback, failCallback) {
    switch (this.status) {
      case FULFILLED:
        successCallback(this.value)
        break
      case REJECTED:
        failCallBack(this.error)
        break
        // 異步狀況處理
      case PENDING:
        this.successCallback.push(successCallback)
        this.failCallback.push(failCallback)
        break
    }
  }
}
// test
const promise = new MyPromise((reslove, reject) => {
  setTimeout(()=> {
    reslove('reslove')
  }, 2000)  
})
promise.then(value => {
  console.log('第一次調用then: ', value)
})
promise.then(value => {
  console.log('第二次調用then: ', value)
})
promise.then(value => {
  console.log('第三次調用then: ', value)
})
// 第一次調用then: reslove
// 第二次調用then: reslove
// 第三次調用then: reslove 
複製代碼

除了屢次調用外,promise還支持鏈式調用。而且後面then方法的回調函數拿到的值是上一個then方法的回調函數的返回值。優化

第四版 - 鏈式調用

這裏值得注意的是,咱們須要判斷上一個then的返回值是什麼類型。若是是普通的值,能夠直接調用reslove方法。若是是promise對象,則須要根據promise對象返回的結果來決定調用reslove或者reject。ui

const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
  constructor(exector) {
    exector(this.reslove, this.reject)
  }
  status = PENDING // promise狀態
  value = undefined // 成功時的值 then方法須要返回
  error = undefined // 失敗時的值 then方法須要返回
  successCallback = [] // 成功回調
  failCallback = [] // 失敗回調
  // 成功時的函數
  reslove = value => {
    // 修改promise狀態
    this.status = FULFILLED
    this.value = value
    while(this.successCallback.length) this.successCallback.shift()(this.value)
  }
  // 失敗時的函數
  reject = error => {
    // 修改promise狀態
    this.status = REJECTED
    this.error = error
    while(this.failCallback.length) this.failCallback.shift()(this.error)
  }
  then(successCallback, failCallback) {
    return new Promise((reslove, reject) => {
      switch (this.status) {
        case FULFILLED:
          reslovePromise(successCallback(this.value), reslove, reject)  
          break
        case REJECTED:
          failCallBack(this.error)
          break
          // 異步狀況處理
        case PENDING:
          this.successCallback.push(successCallback)
          this.failCallback.push(failCallback)
          break
      }
    })  
  }
}
// 通用方法 - 解析promise對象 (PENDING、 FULFILLED、REJECTED都會用到)
function reslovePromise(x, reslove, reject) {
  if (x instanceof MyPromise) {
    x.then(reslove, reject)
  } else {
    reslove(x)
  }
}
// test
const promise = new MyPromise((reslove, reject) => {
  reslove('reslove')    
})
promise.then(value => {
  console.log('第一次調用then: ', value)
  return 'reslove2'
}).then(value => {
  console.log('第二次調用then: ', value)
  return new MyPromise((reslove, reject) => {
    reslove('reslove3')
  })
}).then(value => {
  console.log('第三次調用then: ', value)
})
// 第一次調用then: reslove
// 第二次調用then: reslove2
// 第三次調用then: reslove3
複製代碼

到第四版,咱們的MyPromise實現了promise的基本功能。但從代碼能夠看出,咱們並未對MyPromise作任何錯誤處理,這並非一段健壯的代碼該有的樣子。那麼接下來,咱們就對MyPromise作一些代碼優化及錯誤處理。this

第五版 - 代碼優化及錯誤處理

錯誤處理想到了兩方面。一是在reslove方法執行報錯時,將狀態改成REJECTED,執行reject方法。二是識別promise返回自對象。spa

const PENDING = 'pending'
const FULFILLED = 'fufilled'
const REJECTED = 'rejected'
class MyPromise {
    constructor(exector) {
        try {
            exector(this.reslove, this.reject)
        } catch(e) {
            this.reject(e)
        }
    }

    status = PENDING // promise狀態
    value = undefined // 成功時的值 then方法須要返回
    error = undefined // 失敗時的值 then方法須要返回
    successCallback = [] // 成功回調
    failCallback = [] // 失敗回調
    // 成功時的函數
    reslove = value => {
        // 修改promise狀態
        this.status = FULFILLED
        this.value = value
        while(this.successCallback.length) this.successCallback.shift()()
    }
    // 失敗時的函數
    reject = error => {
        // 修改promise狀態
        this.status = REJECTED
        this.error = error
        while(this.failCallback.length) this.failCallback.shift()()
    }
    then(successCallback, failCallback) {
        // 將then方法的參數變爲可選參數
        successCallback = successCallback ? successCallback : value => value
        failCallback = failCallback ? failCallback : error => {throw error}
        let newPromise = new MyPromise((reslove, reject) => {
            switch (this.status) {
                case FULFILLED:
                    setTimeout(()=> {
                        try {
                            reslovePromise(newPromise, successCallback(this.value), reslove, reject)
                        } catch(e) {
                            reject(e)
                        }       
                    }, 0)               
                    break
                case REJECTED:
                setTimeout(()=> {
                        try {
                            reslovePromise(newPromise, failCallback(this.error), reslove, reject)
                        } catch(e) {
                            reject(e)
                        }       
                    }, 0)
                    break
                // 異步狀況處理
                case PENDING:
                    this.successCallback.push(()=> {
                        setTimeout(()=> {
                            try {
                                reslovePromise(newPromise, successCallback(this.value), reslove, reject)
                            } catch(e) {
                                reject(e)
                            }       
                        }, 0)
                    })
                    this.failCallback.push(()=> {
                        setTimeout(()=> {
                            try {
                                reslovePromise(newPromise, failCallback(this.error), reslove, reject)
                            } catch(e) {
                                reject(e)
                            }       
                        }, 0)
                    })
                    break
            }
        })
        return newPromise   
    }
}
// 通用方法 - 解析promose對象 (PENDING、 FULFILLED、REJECTED都會用到)
function reslovePromise(newPromise ,x, reslove, reject) {
    if (newPromise === x) {
        return reject(new TypeError('循環調用'))
    }
    if (x instanceof MyPromise) {
        x.then(reslove, reject)
    } else {
        reslove(x)
    }
}
// test
const promise = new MyPromise((reslove, reject) => {
    setTimeout(()=> {
        reslove('reslove')
    }, 2000)

    // reject('reject')
})
promise.then(value => {
    console.log('第一次調用then reslove: ', value)
    throw new Error('then error')
}, error => {
    console.log('第一次調用then reject: ', error)
}).then(value => {
    console.log('第二次調用then reslove: ', value)
}, error => {
    console.log('第二次調用then reject: ', error)
})
// 第一次調用then reslove: reslove
// 第二次調用then reject: Error: then error
複製代碼
相關文章
相關標籤/搜索