簡單實現 Promises/A+ 規範

做者:@gauseen前端

Promises/A+ 規範可在這裏查看git

promise3 個狀態,分別爲 pending, fulfilledrejectedgithub

  • promise 在 pending 狀態promise

    • 能夠切換到 fulfilledrejected 狀態
  • promise 在 fulfilled 狀態異步

    • 不能夠切換到其它狀態
    • 必須有個不能夠更改的 value 值
  • promise 在 rejected 狀態函數

    • 不能夠切換到其它狀態
    • 必須有個不能夠更改的 reason 值
// promise 三種狀態
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

// MyPromise 構造函數
function MyPromise (fn) {
  // 初始化狀態
  this.state = PENDING
  this.result = void 0
  this.handlerQueue = []

  let resolve = (value) => {
    transitionState(this, FULFILLED, value)
  }

  let reject = (reason) => {
    transitionState(this, REJECTED, reason)
  }

  // 調用 Promise 構造函數回調
  try {
    fn(resolve, reject)
  } catch (error) {
    reject(error)
  }
}

狀態遷移方法,即調用了 fn(resolve, reject) 中的 resolve, reject 方法後,須要改變 promise 狀態:this

pending --> fulfilledspa

pending --> rejectedprototype

function transitionState (promise, state, result) {
  if (promise.state !== PENDING) return
  promise.state = state
  promise.result = result

  // 這裏先佔個坑位
}

then 方法返回的是一個新的 Promise 實例(注意:不是原來那個 Promise 實例),只有這樣才能不斷的鏈式調用,依次改變狀態code

MyPromise.prototype.then = function (onFulfilled, onRejected) {
  return new MyPromise((resolve, reject) => {
    let handler = { onFulfilled, onRejected, resolve, reject }
    // 若當前狀態爲 pending 則將其放在 handlerQueue 隊列中,等待 resolve 或 reject 方法改變其狀態
    // 不然直接調用 then 方法中的 resolve 或 reject 回調函數
    if (this.state ==== PENDING) {
      this.handlerQueue.push(handler)
    } else {
      dispatchHandler(handler, this.state, this.result)
    }
  })
}
const isFunction = arg => typeof arg === 'function'

function dispatchHandler (handler, state, result) {
  let { onFulfilled, onRejected, resolve, reject } = handler

  if (state === FULFILLED) {
    let finalValue = isFunction(onFulfilled) ? onFulfilled(result) : result
    resolve(finalValue)
  } else if (state === REJECTED) {
    let finalReason = isFunction(onRejected) ? onRejected(result) : result
    reject(finalReason)
  }
}

以上代碼,只支持 Promise 回調函數參數 resolvereject 同步調用的狀況,以下示例代碼:

// 支持

let myPromise = new MyPromise((resolve, reject) => {
  // resolve 同步調用
  resolve('同步調用 value')
})

myPromise.then((value) => {
  console.log('value: ', value)
}, (reason) => {
  console.log('reason: ', reason)
})

可是,使用異步調用不支持,以下示例代碼:

// 暫不支持

let myPromise = new MyPromise((resolve, reject) => {
  // resolve 異步調用
  setTimeout(() => {
    resolve('異步調用 value')
  })
})

myPromise.then((value) => {
  console.log('value: ', value)
}, (reason) => {
  console.log('reason: ', reason)
})

之因此不支持異步調用 resolve 或 reject,是由於 then 方法中以下代碼片斷:

// 當 resolve 爲異步調用,then 方法執行時,promise 狀態爲 pending。
  // 因此 then 回調函數 onFulfilled 和 onRejected 在 handlerQueue 隊列裏,沒有被調用
  if (this.state ==== PENDING) {
    this.handlerQueue.push(handler)
  } else {
    // ...
  }

爲支持 resolvereject 異步調用,狀態遷移方法 transitionState,作以下修改:

function transitionState (promise, state, result) {
  if (promise.state !== PENDING) return
  promise.state = state
  promise.result = result

  // 新增代碼開始
  promise.handlerQueue.forEach(handler => {
    dispatchHandler(handler, state, result)
  })
  // 新增代碼結束
}

由於 catch 方法是 .then(null, onRejected) 的別名,因此實現 catch 代碼以下:

MyPromise.prototype.catch = function (onRejected) {
  return this.then(null, onRejected)
}

如上,簡單實現了 promise,支持鏈式調用 then 和 catch

歡迎關注無廣告文章公衆號:學前端

參考

promise-aplus-impl

相關文章
相關標籤/搜索