本身實現一個Promise

最近看到好多講解Promise源碼解析,或者本身實現一個Promise的文章,忽然想本身造個輪子試一試。javascript

先說明一下,該輪子並不徹底遵照任何標準和規範,只是對標準Promise使用後的理解和感覺而編寫的,純屬興趣研究。java

下面是實現代碼:node

// 對象類型判斷
const is = (typeAsString) => obj => Object.prototype.toString.call(obj) === typeAsString

// 判斷是否爲一個Error對象
const isError = is('[object Error]')


/**
 * 自定義Promise對象
 * @param {(resolve: (value: any) => void, reject: (reason: any) => void) => void} executor 
 */
function MyPromise(executor) {
  this.executor = executor
  // 初始狀態爲pending...
  this.status = 'pending'

  /**
   * 內部斷定狀態,只有處於pending狀態纔可繼續執行
   * @param {number} value 
   */
  function resolve(value) {
    if (this.status === 'pending') { // 確保只執行一次
      this.onfulfilled = ___onfulfilled.call(this, value)
    }
  }

  /**
   * 爲了緩存resolve方法的執行結果
   * @param {*} value value就是resolve方法的執行結果
   */
  function ___onfulfilled(value) {
    this.status = 'fulfilled'   // 更改內部狀態
    /**
     * @param {(value: number) => void} onfulfilled 這裏是then方法中傳入的參數
     */
    return (onfulfilled) => {
      return onfulfilled(value) // 
    }
  }

  /**
   * 觸發異常的方法
   * @param {string} reason 
   */
  function reject(reason) {
    if (this.status === 'pending') {  // 確保只執行一次
      this.onrejected = ___onrejected.call(this, reason)
    }
  }

  /**
   * 
   * @param {Error} reason 若是傳入的不是一個Error對象,那麼會使用Error包裝一下
   */
  function ___onrejected(reason) {
    this.status = 'rejected'    // 更改內部狀態
    return (onrejected) => {
      reason = isError(reason) ? reason : new Error(reason)
      return onrejected(reason)
    }
  }


  /**
   * @param {(value: number) => any} onfulfilled 處理成功的函數
   * @param {(reason: any) => void} onrejected 處理出現異常的函數,若是傳入該參數,那麼catch方法就捕獲不到了
   */
  this.then = function(onfulfilled, onrejected) {
    const self = this
    return new MyPromise((resolve, reject) => {
      setTimeout(function waitStatus() {
        switch (self.status){
          case 'fulfilled': // resolved
            if (onfulfilled) {
              // 將onfulfilled方法的返回值交給resolve,確保下一個.then方法可以獲得上一個.then方法的返回值
              const nextValue = self.onfulfilled(onfulfilled) 
              resolve(nextValue)
            } else {
              resolve()   // 沒有傳入參數,僞裝傳入了參數:)
            }
            break
          case 'rejected':  // rejected
            if (!onrejected) { // 若是沒有傳遞onrejected參數,默認實現一個,確保catch方法可以捕獲到
              onrejected = (reason) => reason
            }
            const nextReject = self.onrejected(onrejected)
            reject(nextReject)
            break
          case 'pending':   // 
          default:
              setTimeout(waitStatus, 0) // 只要是pending狀態,繼續等待,直到不是pending
              break
        }
      }, 0);
    })
  }

  /**
   * 捕獲異常
   * @param {(reason: any) => void} onrejected
   */
  this.catch = function(onrejected) {
    const self = this
    setTimeout(function reject() {
      if (self.status === 'rejected') {
        self.onrejected(onrejected)
      } else {
        setTimeout(reject, 0);
      }
    }, 0);
  }

  // 直接執行
  this.executor(resolve.bind(this), reject.bind(this));
}

目前不考慮參數傳入的正確性,假設傳入的參數所有是正確的狀況下,在nodejs環境下可以正常運行。如下是測試代碼:git

// 覆蓋nodejs環境中的標準Promise對象
global.Promise = MyPromise

async function test () {
  const r = await new Promise((resolve) => {
    setTimeout(() => {
      resolve(121)
    }, 1000);
  })
  console.log(r)    // 打印121
}

test()

以上代碼主要是使用setTimeout方法去檢查Promise對象內部的狀態,一旦發生變化當即做出相應的處理。github

由於是靠setTimeout方法檢查Promise內部的狀態,因此屬於宏指令任務,執行的優先級不像標準Promise那麼高。緩存

setTimeout(() => {
  console.log(1)
}, 0);

new MyPromise((resolve, reject) => {
  console.log(2)
  resolve(3)
}).then(v => {
  console.log(v)
})

上面代碼的輸出是2,1,3
標準Promise的輸出結果應該是2,3,1async

源代碼在此。謝謝觀看!函數

相關文章
相關標籤/搜索