简单版 Promise/A+,通过官方872个测试用例
promise 标准
在实现 Promise 之前要清楚的是 JavaScript 中的 Promise 遵循了 Promises/A+ 规范,所以我们在编写 Promise 时也应当遵循这个规范,建议认真、仔细读几遍这个规范。最好是理解事件循环,这样对于理解js中的异步是怎么回事非常重要。
基本使用
new Promise( function(resolve, reject) {...} /* executor */ );
new Promise((resolve, reject)=> {
AjaxRequest.post({
url: 'url',
data: {},
sueccess: ()=> {
resolve(res)
},
fail: (err)=> {
reject(err)
}
})
}).then((res)=> {
// do some
}).then(value => { }).catch((err)=> {
// do some
})
promise 是处理异步结果的一个对象,承若状态改变时调用对应的回调函数,resolve、reject用来改变promise 的状态,then 绑定成功、失败的回调。
环境准备
安装测试工具以及nodemon因为我们要在node环境调试自己写的promise
// nodemon
npm install nodemon -D
// promise 测试工具
npm install promises-aplus-tests -D
增加脚本命令
"testPromise": "promises-aplus-tests myPromise/promise3.js",
"dev": "nodemon ./myPromise/index.js -i "
各自的路径改成自己的即可,这个在后面会用来测试。
基本架子
根据规范实现一个简单的promise,功能如下
- promise的三种状态(PENDING、FULFILLED、REJECTED)
- 状态只能由 Pending 变为 Fulfilled 或由 Pending 变为 Rejected ,且状态改变之后不会在发生变化,会一直保持这个状态
- 绑定then的回调
- 返回成功、失败的值
- 一个promise 支持调用多次then
- 支持捕获异常
/* 基本架子
根据promise A+ 规范还要处理then链式调用以及返回值传递的问题,后续在promise2、promise3 处理 */ const PENDING = 'PENDING',
FULFILLED = 'FULFILLED',
REJECTED = 'REJECTED'; class myPromise {
constructor (executor) {
this.status = PENDING
this.value = undefined
this.reason = undefined
this.onResolveCallbacks = []
this.onRejectedCallbacks = [] const resolve = (value) => {
if (this.status === PENDING) {
this.status = FULFILLED
this.value = value
// 发布
this.onResolveCallbacks.forEach(fn => fn())
}
} const reject = (reason) => {
if (this.status === PENDING) {
this.status = REJECTED
this.reason = reason
// 发布
this.onRejectedCallbacks.forEach(fn => fn())
}
} try {
// 执行传进来的fn, 在给他提供改变状态的fn
executor(resolve, reject)
} catch(e) {
reject(e)
}
}
// 订阅回调函数
then (onFulfilled, onRejected) { if (this.status = PENDING) {
// 订阅
this.onResolveCallbacks.push(() => {
onFulfilled(this.value)
}) this.onRejectedCallbacks.push(() => {
onRejected(this.reason)
})
} if (this.status === FULFILLED) {
onFulfilled(this.value)
} if (this.status === REJECTED) {
onRejected(this.reason)
}
}
} module.exports = myPromise
订阅
传进来的fn是一个执行器,接受resolve、reject参数,通常我们在构造函数中需要调用某个接口,这是一个异步的操作,执行完构造函数之后,在执行then(),这个时候的状态还是pending,所以我们需要把then 绑定的回调存起来,也可以理解为promise对象订阅了这个回调。
发布
在 resolve,reject函数中中我们改变了promise 对象的状态,既然状态改变了,那么我们需要执行之前订阅的回调,所以在不同的状态下执行对应的回调即可。
流程
如上所示,实例化对象,执行构造函数,碰到异步,挂起,然后执行then()方法,绑定了resolve、reject的回调。如果异步有了结果执行对应的业务逻辑,调用resolve、或者reject,改变对应的状态,触发我们绑定的回调。
以上就是最基本的promise架子,但是还有promise 调用链没有处理,下面继续完善...
完善promise 调用链
promose 的精妙的地方就是这个调用链,首先then 函数会返回一个新的promise 对象,并且每一个promise 对象又有一个then 函数。惊不惊喜原理就是那么简单,回顾下then的一些特点
then 特点
- then 返回一个新的promise 对象
- then 绑定的回调函数在异步队列中执行(evnet loop 事件循环)
- 通过return 来传递结果,跟fn一样如果没有return,默认会是 underfined
- 抛出异常执行绑定的失败函数(最近的promise),如果没有,则执行catch
- then中不管是不是异步只要resolve、rejected 就会执行对应 onFulfilled、onRejected 函数
- then中返回promise状态跟执行回调的结果有关,如果没有异常则是FULFILLED,就算没有retun 也是FULFILLED,值是underfined,有异常就是REJECTED,接着走下个then 绑定的onFulfilled 、onRejected 函数
根据上面的特点以及阅读规范我们知道then()函数主要需要处理以下几点
- 返回一个新的promise
- 值怎么传给then返回的那个promise
- 状态的改变
返回一个新的promise
因为promise 的链式调用涉及到状态,所以then 中返回的promise 是一个新的promise
then(onFulfilled, onRejected) {
let promise2 = new Promise((resolve, reject) => {
// do ...
})
return promise2
}
值的传递、状态的改变
let p = new myPromise((resolve, rejected) => {
// do ...
})
p.then(
value => {
return 1
},
reason => {}
)
.then(
value => {
return new Promise((resolve, rejected) => {
resolve('joel')
})
},
reason => {}
)
.then(
value => {
throw 'err: 出错啦'
},
reason => {}
)
then 返回的值可能是一个普通值、promise对象、function、error 等对于这部分规范文档也有详细的说明
[[Resolve]](promise, x)
这个可以理解为promise 处理的过程,其中x是执行回调的一个值,promise 是返回新的promise对象,完整代码如下
我们将这部分逻辑抽成一个独立的函数 如下
// 处理then返回结果的流程
function resolvePromise(promise2, x, resolve, reject) {
if (promise2 === x) {
return reject(new TypeError('Chaining cycle detected for promise #<myPromise>'))
} let called = false if ((typeof x === 'object' && x !== null) || typeof x === 'function') {
try {
let then = x.then
// 判断是否是promise
if (typeof then === 'function') {
then.call(x, (y) => {
// 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
if (called) return
called = true
resolvePromise(promise2, y, resolve, reject)
}, (r) => {
if (called) return
called = true
reject(r)
})
} else {
resolve(x)
}
} catch (e) {
if (called) return
called = true
reject(e)
}
} else {
// 如果 x 不为对象或者函数,以 x 普通值执行回调
resolve(x)
}
}
测试
promises-aplus-tests 这个工具我们必须实现一个静态方法deferred,官方对这个方法的定义如下:
deferred: 返回一个包含{ promise, resolve, reject }的对象
promise 是一个处于pending状态的promise
resolve(value) 用value解决上面那个promise
reject(reason) 用reason拒绝上面那个promise
添加如下代码
myPromise.defer = myPromise.deferred = function () {
let deferred = {} deferred.promise = new myPromise((resolve, reject) => {
deferred.resolve = resolve
deferred.reject = reject
})
return deferred
}
在编辑执行我们前面加的命令即可
npm run testMyPromise
完善其他方法
- all
- allSettled
- any
- race
- catch
- finlly
npm run dev // 可以用来测试这些方法
源码
参考
https://www.jianshu.com/p/4d266538f364
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise/all
简单版 Promise/A+,通过官方872个测试用例的更多相关文章
- 手写一个Promise/A+,完美通过官方872个测试用例
前段时间我用两篇文章深入讲解了异步的概念和Event Loop的底层原理,然后还讲了一种自己实现异步的发布订阅模式: setTimeout和setImmediate到底谁先执行,本文让你彻底理解Eve ...
- 手写简易版Promise
实现一个简易版 Promise 在完成符合 Promise/A+ 规范的代码之前,我们可以先来实现一个简易版 Promise,因为在面试中,如果你能实现出一个简易版的 Promise 基本可以过关了. ...
- echarts怎么使用(最最最最简单版)(本质canvas)
echarts怎么使用(最最最最简单版)(本质canvas) 一.总结 一句话总结:外部扩展插件肯定要写js啊,不然数据怎么进去,不然宽高怎么设置.本质都是canvas嵌套在页面上,比如div中. 1 ...
- 全栈前端入门必看 koa2+mysql+vue+vant 构建简单版移动端博客
koa2+mysql+vue+vant 构建简单版移动端博客 具体内容展示 开始正文 github地址 <br/> 觉得对你有帮助的话,可以star一下^_^必须安装:<br/> ...
- JavaMail简单版实验测试
前言: 最近由于实现web商城的自动发送邮件功能的需求,故涉猎的邮箱协议的内部原理.现将简单版的Java Mail实例做个代码展示,并附上其中可能出现的bug贴出,方便感兴趣的读者进行测试! 1.载入 ...
- 小米抢购(简单版v0.1)-登录并验证抢购权限,以及获取真实抢购地址
小米(简单版)-登录并验证抢购权限,以及获取真实抢购地址! 并不是复制到浏览器就行了的 还得传递所需要的参数 这里只是前部分 后面的自己发挥了 { "stime": 1389 ...
- 一个简单的Promise 实现
用了这么长时间的promise,也看了很多关于promise 的文章博客,对promise 算是些了解.但是要更深的理解promise,最好的办法还是自己实现一个. 我大概清楚promise 是对异步 ...
- Java实现简单版SVM
Java实现简单版SVM 近期的图像分类工作要用到latent svm,为了更加深入了解svm,自己动手实现一个简单版的. 之所以说是简单版,由于没实用到拉格朗日,对偶,核函数等等.而 ...
- Microsoft Visual Studio 2012旗舰版(VS2012中文版下载)官方中文版
Microsoft Visual Studio 2012 Ultimate旗舰版(VS2012中文版下载)是一个最先进的开发解决方案,它使各种规模的团队能够设计和创建出使用户欣喜的引人注目的应用程序. ...
随机推荐
- 团队作业5:Alpha版本测试和发布(歪瑞古德小队)
目录 一.项目文档和代码 二.Alpha版本测试报告 2.1 功能测试 2.1.1 功能列表 2.1.2 场景测试 2.1.3 测试结果 2.1.4 bug清单 2.2 兼容性测试 2.3 性能测试 ...
- 软工团队项目之团队展示&选题(OnTime——S.L.N)
软工团队项目之团队展示&选题(OnTime——S.L.N) 一.团队展示 队名:『S.L.N』即Seigelion——乃“攻城狮”之意. 队员学号: 团队项目描述:(项目名称:OnTime) ...
- 第2篇scrum
第2篇scrum 一.站立式会议 1.1会议照片 想得美 1.2项目进展 团队成员 昨日完成任务 今日计划任务 感想 吴茂平 完善用户系统 改进评论数据表,增加评论,删除评论,查询评论 今天也是元气满 ...
- Spring Boot入门系列(十九)整合mybatis,使用注解实现动态Sql、参数传递等常用操作!
前面介绍了Spring Boot 整合mybatis 使用注解的方式实现数据库操作,介绍了如何自动生成注解版的mapper 和pojo类. 接下来介绍使用mybatis 常用注解以及如何传参数等数据库 ...
- IDEA常用快捷键Mac os和Windows对照--用到了就会更新
之前公司用了一段的MacBookPro,离职后自己入手了一台MacBookPro.但是现在的公司中使用的电脑是古老的win7,两个系统的键盘有些许差别,而且快捷键也略有不同.最近因为疫情影响,在家远程 ...
- in文件注意事项及详细解释(转载)
转载自:https://www.cnblogs.com/sysu/p/10817315.html 和 https://www.cnblogs.com/panscience/p/4953940.h ...
- h5c3
HTML5 第一天 一.什么是 HTML5 HTML5 的概念与定义 定义:HTML5 定义了 HTML 标准的最新版本,是对 HTML 的第五次重大修改,号称下一代的 HTML 两个概念: 是一个新 ...
- vim编辑器 与etc目录
第1章 目录结构 1.1 vim vim故障 vim 是vi的升级版本 vi类似于文本文档 vim类似于notepad++ 编辑 必须先安装vim命令 yum -y insta ...
- Linux安装doker
docker安装(centos) 官方文档:https://docs.docker.com/engine/install/centos/ 前提条件 内核系统3.10以上的centos7.可用 unam ...
- 使用Telnet服务测试端口时,提示没有Telnet服务
1.win7系统是默认不开启Telnet服务的,所以我们第一次使用时要手动开启Telnet服务 1)打开 控制面板 > 程序 > 程序功能 > 打开或关闭Windows功能,勾选上T ...