Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

简单实现 Promises/A+ 规范 #15

Open
gauseen opened this issue Oct 12, 2019 · 0 comments
Open

简单实现 Promises/A+ 规范 #15

gauseen opened this issue Oct 12, 2019 · 0 comments
Assignees
Labels
javascript javascript 原理 原理

Comments

@gauseen
Copy link
Owner

gauseen commented Oct 12, 2019

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'

const delay = (fn, time = 0) => (...args) => {
    setTimeout(() => fn(...args), time)
}

// 异步执行 then 回调
const dispatchHandler = delay((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

@gauseen gauseen added 原理 原理 javascript javascript labels Oct 12, 2019
@gauseen gauseen self-assigned this Oct 18, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
javascript javascript 原理 原理
Projects
None yet
Development

No branches or pull requests

1 participant