Promise/A+ 规范
Promise/A+ 是关于 JS promise 的一个标准规范,ES6 promise实际上就是此规范的一个超集,理解这个规范能帮助你更好的理解ES6 promise
实现代码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
function PromiseDLL(fn) {
this.state = PENDING
this.value = null
this.reason = null
this.onFulfilled = null
this.onRejected = null
try {
fn(this.resolve, this.reject)
} catch (err) {
this.reject(err)
}
}
PromiseDLL.prototype.resolve = function (value) {
if (this.state !== PENDING) {
return
}
this.onFulfilled && this.onFulfilled(this.value)
this.value = value
this.state = FULFILLED
}
PromiseDLL.prototype.reject = function (reason) {
if (this.state !== PENDING) {
return
}
this.onRejected && this.onRejected(this.value)
this.reason = reason
this.state = REJECTED
}
PromiseDLL.prototype.then = function (onFulfilled, onRejected) {
const that = this
return new Promise((resolve, reject) => {
function resolveP(x) {
if (x === this) {
throw TypeError('cannot be same object')
} else {
const then = x.then
if ((typeof x === 'object' || typeof x === 'function') && typeof then === 'function') {
then.call(x, resolve, reject)
} else {
resolve(x)
}
}
}
function fulfillHandler(value) {
if (typeof onFulfilled === 'function') {
resolveP(onFulfilled(value))
} else {
resolve(value)
}
}
function rejectHandler(reason) {
if (typeof onRejected === 'function') {
resolveP(onRejected(reason))
} else {
reject(reason)
}
}
switch (this.state) {
case PENDING:
that.onFulfilled = fulfillHandler
that.onRejected = rejectHandler
break
case FULFILLED:
setTimeout(() => {
fulfillHandler(that.value)
}, 0)
break
case REJECTED:
setTimeout(() => {
rejectHandler(this.reason)
}, 0)
break
default:
throw TypeError('undefined promise state')
}
})
}
Some Bulls**t
我本人的实现过程进展比较顺利,测试用例也一遍就全过了(偷偷开心),可能因为刚好前几天已经在写中英对照翻译的过程里通读了若干遍Promise/A+ 规范原文,但是一篇文章只放代码有点干巴巴,还是觉得要说一下巴拉巴会过瘾一点,所以以下写了一些实现过程中的思考:
- 规范中拆分到多个章节的一些规则,其实是前后串联互相呼应的,比如对于
onFulfilled/onRejected抛错相关的描述,在实现中发现这段代码已经可以 cover 住各种抛错的处理规则,所以不需要在实现try { fn(this.resolve, this.reject) } catch (err) { this.reject(err) }then/resolve/reject方法时再额外去做异常处理了,这其实也说明Promise/A+规范本身是非常优雅的 - Promise/A+ 规范中废弃了
promise2 = promise1.then(...)中promise2 !== promise1的要求(见Omissions - 1.3),这里在实现then方法时曾想过直接返回this来避免频繁创建新的Promise实例,但是当时思考到一半没有继续下去,后面如果有时间把这一部分想清楚再补上 then方法可以通过宏任务或微任务实现都可以(Promise/A+ 注释 3.1明确表示这里不做要求),这里使用了setTimeout来满足规则 2.2.4,但是如果希望基于微任务来实现,可以考虑使用其它方法(这里我预感到又可以写一篇关于宏任务微任务的废话)
测试 Promise/A+
Promise/A+ 组织提供了官方测试用例,使用方式也非常简单,这里使用其提供的adapters 方式来测试,代码如下
// Adapter
PromiseDLL.deferred = () => {
const res = {}
res.promise = new Promise((resolve, reject) => {
Object.assign(res, { resolve, reject })
})
return res
}
module.exports = PromiseDLL
// package.json
{
...
"scripts": {
...
"test": "promises-aplus-tests promise-aplus",
...
},
...
}
872 个测试用例全部通过