简单实现 Promises/A+ 规范

作者:@gauseen

Promises/A+规范可在这里查看

promise3个状态,分别为 pending, fulfilledrejected

  • promise 在 pending状态

    • 可以切换到 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状态:

pending --> fulfilled

pending --> rejected

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

  // 这里先占个坑位
}

then方法返回的是一个新的 Promise实例(注意:不是原来那个 Promise实例),只有这样才能不断的链式调用,依次改变状态

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

原文链接:segmentfault.com

上一篇:深入理解 Eslint (手把手教你撸一个plugin)
下一篇:使用webpack将ES6转化ES5

相关推荐

官方社区

扫码加入 JavaScript 社区