手寫 Promise

就是干

ECMAscript 6 原生提供了 Promise 對象。Promise 對象代表了未來將要發生的事件,用來傳遞異步操作的消息。
接下來用分塊、分步驟、加註釋來一步一步,實現手寫 Promise。

一、實現 Promise 的基本使用

  1. Promise 就是一個類在執行這個類的時候需要傳遞一個執行器進去,執行器會立即執行
  2. Promise 中有三種狀態 分別為 等待(pending) 成功(fulfilled)失敗(rejected)
    一旦狀態確定就不可更改
    pending -> fulfilled
    pending -> rejected
  3. resolve 和 reject 函數是用來更改狀態的
    resolve: fulfilled
    reject: rejected
  4. then 方法內部做的事情就判斷狀態,如果狀態成功就調用成功的回調函數,如果狀態失敗就調用失敗的回調函數 then 方法是被定義在原型對象上面
  5. then 成功和失敗之後都會有一個對應的成功值和失敗值
    先定義三個常量分別代表以下狀態
  • const PENDING = ‘pending’ // 等待
  • const FULFILLED = ‘fulfilled’ // 成功
  • const REJECTED = ‘rejected’ // 失敗
簡單講了一下基本概念,下面貼代碼:
class MyPromise {
  constructor(executor) {
    executor(this.resolve, this.reject)
  }
  status = PENDING // 默認值 等待
  value = undefined // 成功之後的值
  reason = undefined // 失敗後的值
  // resolve,reject 箭頭函數 是為了讓這個函數內部 this 指向這個類的實例對象,也就是 promise
  resolve = (value) => {
    // 如果狀態不是等待 阻止程序繼續執行
    if (this.status !== PENDING) return
    // 將狀態改為成功
    this.status = FULFILLED
    // 保存成功之後的值
    this.value = value
  }
  reject = (reason) => {
    // 如果狀態不是等待 阻止程序繼續執行
    if (this.status !== PENDING) return
    // 將狀態改為成功
    this.status = REJECTED
    // 保存失敗後的原因
    this.reason = reason
  }
  then(successCallback, failCallback) {
    // 判斷狀態
    if (this.status === FULFILLED) {
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      failCallback(this.reason)
    }
  }
}

二、實現 Promise 的異步情況

  1. 處理異步情況 和多個 promise.then 這裡指的是多個 promise 的使用,並不是鏈式調用
class MyPromise {
  constructor(executor) {
    executor(this.resolve, this.reject)
  }
  status = PENDING // 默認值 等待
  value = undefined // 成功之後的值
  reason = undefined // 失敗後的值
  successCallback = [] // 成功回調,存儲多個函數,鏈式調用
  failCallback = [] // 失敗回調,存儲多個函數,鏈式調用
  // resolve,reject 箭頭函數 是為了讓這個函數內部 this 指向這個類的實例對象,也就是 promise
  resolve = (value) => {
    // 如果狀態不是等待 阻止程序繼續執行
    if (this.status !== PENDING) return
    // 將狀態改為成功
    this.status = FULFILLED
    // 保存成功之後的值
    this.value = value
    // 循環存儲的成功回調函數長度,然後依次執行
    while (this.successCallback.length) this.successCallback.shift()(this.value)
  }
  reject = (reason) => {
    // 如果狀態不是等待 阻止程序繼續執行
    if (this.status !== PENDING) return
    // 將狀態改為成功
    this.status = REJECTED
    // 保存失敗後的原因
    this.reason = reason
    // 循環存儲的失敗回調函數長度,然後依次執行
    while (this.failCallback.length) this.failCallback.shift()(this.reason)
  }
  then(successCallback, failCallback) {
    // 判斷狀態
    if (this.status === FULFILLED) {
      successCallback(this.value)
    } else if (this.status === REJECTED) {
      failCallback(this.reason)
    } else {
      // 因為等待期間你不知道後面執行的是成功的回調還是失敗的回調
      // 所以將成功回調和失敗回調存儲起來
      this.successCallback.push(successCallback)
      this.failCallback.push(failCallback)
    }
  }
}

三、實現 Promise 的鏈式調用及捕獲錯誤信息

  1. then 方法的鏈式調用,以及避免 promise 對象自已返回自己 形成循環
  2. 將 then 參數變成可選參數
  3. 捕獲執行器錯誤信息,以及使用 then 時捕獲錯誤信息
class MyPromise {
  constructor(executor) {
    try {
      executor(this.resolve, this.reject)
    } catch (e) {
      this.reject(e)
    }
  }
  status = PENDING // 默認值 等待
  value = undefined // 成功之後的值
  reason = undefined // 失敗後的值
  successCallback = [] // 成功回調,存儲多個函數,鏈式調用
  failCallback = [] // 失敗回調,存儲多個函數,鏈式調用
  // resolve,reject 箭頭函數 是為了讓這個函數內部 this 指向這個類的實例對象,也就是 promise
  resolve = (value) => {
    // 如果狀態不是等待 阻止程序繼續執行
    if (this.status !== PENDING) return
    // 將狀態改為成功
    this.status = FULFILLED
    // 保存成功之後的值
    this.value = value
    // 循環存儲的成功回調函數長度,然後依次執行
    while (this.successCallback.length) this.successCallback.shift()()
  }
  reject = (reason) => {
    // 如果狀態不是等待 阻止程序繼續執行
    if (this.status !== PENDING) return
    // 將狀態改為成功
    this.status = REJECTED
    // 保存失敗後的原因
    this.reason = reason
    // 循環存儲的失敗回調函數長度,然後依次執行
    while (this.failCallback.length) this.failCallback.shift()()
  }
  then(successCallback, failCallback) {
    // 判斷,變成可選參數,假設第一個,第二個 .then 不傳參數,第三個傳參,一樣能拿到數據 
    successCallback = successCallback ? successCallback : value => value
    failCallback = failCallback ? failCallback : reason => { throw reason }
    // 為了鏈式調用返回一個新的 promise 對象
    let newPromise = new MyPromise((resolve, reject) => {
      // 判斷狀態
      if (this.status === FULFILLED) {
        // 這裡用計時器的原因是為了異步調用,因為需要等 newPromise 執行完畢後,才可以把他當參數傳遞給 resolvePromise 這個函數
        setTimeout(() => {
          try {
            let x = successCallback(this.value)
            // 存儲上一個成功回調的返回值,傳遞給下一個 .then
            // 判斷 x 的值是普通值還是 promise 對象
            // 如果是普通值,直接調用 resolve
            // 如果是 promise 對象,查看 promise 對象返回的結果
            // 再根據 promise 對象返回的結果,決定調用 resolve 還是 reject
            // 聲明一個方法,進行操作
            resolvePromise(newPromise, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      } else if (this.status === REJECTED) {
        setTimeout(() => {
          try {
            let x = failCallback(this.reason)
            // 存儲上一個成功回調的返回值,傳遞給下一個 .then
            // 判斷 x 的值是普通值還是 promise 對象
            // 如果是普通值,直接調用 resolve
            // 如果是 promise 對象,查看 promise 對象返回的結果
            // 再根據 promise 對象返回的結果,決定調用 resolve 還是 reject
            // 聲明一個方法,進行操作
            resolvePromise(newPromise, x, resolve, reject)
          } catch (e) {
            reject(e)
          }
        }, 0)
      } else {
        // 因為等待期間你不知道後面執行的是成功的回調還是失敗的回調
        // 所以將成功回調和失敗回調存儲起來
        this.successCallback.push(() => {
          setTimeout(() => {
            try {
              let x = successCallback(this.value)
              // 存儲上一個成功回調的返回值,傳遞給下一個 .then
              // 判斷 x 的值是普通值還是 promise 對象
              // 如果是普通值,直接調用 resolve
              // 如果是 promise 對象,查看 promise 對象返回的結果
              // 再根據 promise 對象返回的結果,決定調用 resolve 還是 reject
              // 聲明一個方法,進行操作
              resolvePromise(newPromise, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
        this.failCallback.push(() => {
          setTimeout(() => {
            try {
              let x = failCallback(this.reason)
              // 存儲上一個成功回調的返回值,傳遞給下一個 .then
              // 判斷 x 的值是普通值還是 promise 對象
              // 如果是普通值,直接調用 resolve
              // 如果是 promise 對象,查看 promise 對象返回的結果
              // 再根據 promise 對象返回的結果,決定調用 resolve 還是 reject
              // 聲明一個方法,進行操作
              resolvePromise(newPromise, x, resolve, reject)
            } catch (e) {
              reject(e)
            }
          }, 0)
        })
      }
    })
    return newPromise
  }
}
function resolvePromise(newPromise, x, resolve, reject) {
  if (newPromise === x) {
    return reject(new TypeError('出錯了大爺,不要自己調自己'))
  }
  if (x instanceof MyPromise) {
    // 判斷是否是實例對象就行
    // 如果成功就調用 resolve,如果失敗就調用 reject
    x.then(resolve, reject)
  } else {
    // 普通值
    resolve(x)
  }
}

四、實現 Promise.all()

  1. 實現 Promise.all()
  2. 實現 Promise.resolve()
  3. 實現 finally()
因為下述代碼只是新增了幾個靜態方法,剩餘代碼同第三步一是樣的,所以只展示新增的變化
class MyPromise {
  // all
  static all (array) {
    // 解決異步並發問題,並且可以按照順序調用,並且該方法是靜態方法
    let result = []
    let index = 0
    return new MyPromise((resolve, reject) => {
      function addData (key, value) {
        result[key] = value
        index++
        if (index === array.length) {
          // 是為等所有的異步操作執行完畢
          resolve(result)
        }
      }
      for (let i = 0; i < array.length; i++) {
        // 循環 all 數組裏面的值
        let current = array[i]
        if (current instanceof MyPromise) {
          // promise 對象
          current.then(value => addData(i, value), reason => reject(reason))
        } else {
          // 普通值
          addData(i, array[i])
        }
      }
    })
  }
  // resolve
  static resolve (value) {
    if (value instanceof MyPromise) return value
    return new MyPromise ((resolve) => resolve(value))
  }
  // finally
  finally (callback) {
    return this.then((value) => {
      return MyPromise.resolve(callback()).then(() => value)
    }, (reason) => {
      return MyPromise.resolve(callback()).then(() => {throw reason})
    })
  }
  catch (failCallback) {
    return this.then(undefined, failCallback)
  }
}

結束 OVER