简单实现 Promises/A+ 规范

2019-10-12 admin

作者:@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

[转载]原文链接:https://segmentfault.com/a/1190000020663191

本站文章除注明转载外,均为本站原创或编译。欢迎任何形式的转载,但请务必注明出处。

转载请注明:文章转载自 JavaScript中文网 [https://www.javascriptcn.com]

本文地址:https://www.javascriptcn.com/read-77240.html

文章标题:简单实现 Promises/A+ 规范

相关文章
JavaScript编辑器推荐
主流编辑器有SublimeText,Notepad++,webstorm等,是使用最广泛的编辑器,但也有一些JavaScript编辑器提供有着各自的特性和功能,适应不同人的需求,以下是几款优秀的编辑器,相信你一定能找到自己喜欢的。 1. W...
2015-11-12
js性能优化 如何更快速加载你的JavaScript页面
确保代码尽量简洁 不要什么都依赖JavaScript。不要编写重复性的脚本。要把JavaScript当作糖果工具,只是起到美化作用。别给你的网站添加大量的JavaScript代码。只有必要的时候用一下。只有确实能改善用户体验的时候用一下。 ...
2015-11-12
2015年JavaScript或“亲库而远框架”
2014年过去了,作为一个JavaScript开发者很难满怀信心的去“挽回”一个特定的库或技术,即便是强大的Angular,似乎也因为最近的一些事情而动摇。 2014年10月的ng-europe会议上,Angular开发者团队透露了一个关于...
2015-11-12
Android中Okhttp3实现上传多张图片同时传递参数
之前上传图片都是直接将图片转化为io流传给服务器,没有用框架传图片。 最近做项目,打算换个方法上传图片。 Android发展到现在,Okhttp显得越来越重要,所以,这次我选择用Okhttp上传图片。 Okhttp目前已经更新到Okhttp...
2017-03-17
v-charts | 饿了么团队开源的基于 Vue 和 ECharts 的图表工具
在使用echarts生成图表时,经常需要做繁琐的数据类型转化、修改复杂的配置项,v-charts的出现正是为了解决这个 痛点。基于Vue2.0和echarts封装的v-charts图表组件,只需要统一提供一种对前后端都友好的数据格式 设置简...
2018-05-24
JavaScript实现PC手机端和嵌入式滑动拼图验证码三种效果
PC和手机端网站滑动拼图验证码效果源码,同时包涵了弹出式Demo,使用ajax形式提交二次验证码所需的验证结果值,嵌入式Demo,使用表单形式提交二次验证所需的验证结果值,移动端手动实现弹出式Demo三种效果 首先要确认前端使用页面,比如...
2017-03-17
Angular2-primeNG文件上传模块FileUpload使用详解
近期在学习使用Angular2做小项目,期间用到很多primeNG的模块。 本系列将结合实战总结angular2-primeNG各个模块的使用经验。 文件上传模块FileUploadModule 首先要在使用该组件的模块内导入文件上传模块 ...
2017-03-09
Vue.js组件tab实现选项卡切换
本文实例为大家分享了vue插件tab选项卡的具体代码,供大家参考,具体内容如下 效果图: 代码如下: <!DOCTYPE html> <html lang="en"> <head> ...
2017-03-13
React.js编程思想
JavaScript框架层出不穷,在很多程序员看来,React.js是创建大型、快速的Web应用的最好方式。这一款由Facebook出品的JS框架,无论是在Facebook还是在Instagram中,它的表现都非常出色。 使用React.j...
2015-11-12
JavaScript常用特效chm下载
下载地址:JavaScript常用特效chm下载 对了,如果打开空白,在手册上右键属性解除锁定即可。 ...
2015-11-12
回到顶部