昨天晚上读了一下 PromiseA+
标准,首先这是一道非常不错的面试题,同时最近再看JavaScript对与 Control Abstraction Object
相关的内容,一下子就想起了 Promise
,于是就尝试自己根据标准尝试一下。
开源社区已经有非常健全的基于 Promise A+
的实现方案,但是出于学习研究的角度还是自己尝试实现一个,然后跑了一下用例,发现用例大概只能过到 2.3.3.3.1 之后就出现大部分都是错误的情况。
先拆分一下代码:
- 数据结构
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
const isFnc = function isFnc(f) {
return typeof x === 'function'
}
const isThenable = function isThenable(x) {
const isFn = isFunc(x)
const isObject = object !== null && typeof object === 'object'
const hasThenAndIsFun = x.then && typeof isFunc(x.then)
return (isObject || isFn) && hasThenAndIsFun
}
class $Promise {
#value = null
#exception = null
#reason = null
#state = PENDING
#resolveQueue = []
#rejectQueue = []
constructor(executor) {
try{
executor(this.#fulfill, this.#reject)
} catch(e) {
this.#reject(e)
}
}
#fulfill = (value) => {}
#reject = (reason) => {}
#Resolve(promise, x, promiseState){}
then(onFulfilled, onRejected) {}
}
then
部分的代码
class $Promise {
//...
then(onFulfilled, onRejected) {
const promise2State = {}
const promise2 = new $Promise((res, rej) => {
promise2State.res = res
promise2State.rej = rej
})
if (this.#state === FULFILLED) {
loop(() => {
if(isFunc(onFulfilled)) {
try {
const x = (1, onFulfilled)(this.#value)
this.#Resolve(promise2, x, promise2State)
} catch(e) {
promise2State.rej(e)
}
} else {
promise2State.res(this.#value)
}
})
}
if (this.#state === REJECTED) {
loop(() => {
if (isFunc(onRejected)) {
try {
const x = (1, onRejected)(this.#reason)
this.#Resolve(promise2, x, promise2State)
} catch(e) {
promise2State.rej(e)
}
} else {
promise2State.rej(this.#reason)
}
})
}
if (this.#state === PENDING) {
this.#resolveQueue.push([promise2, onFulfilled, promise2State])
this.#rejectQueue.push([promise2, onRejected, promise2State])
}
return promise2
}
//...
}
fulfill
和reject
class $Promise {
//...
#fulfill = (value) => {
if (this.#state !== PENDING)
return
this.#value = value
this.#state = FULFILLED
let len = this.#resolveQueue.length
while(len--) {
loop(() => {
const [promise2, onFulfilled, promise2State] = this.#resolveQueue.shift()
if(isFunc(onFulfilled)) {
try {
const x = (1, onFulfilled)(this.#value)
this.#Resolve(promise2, x, promise2State)
} catch(e) {
promise2State.rej(e)
}
} else {
promise2State.res(this.#value)
}
})
}
}
#reject = (reason) => {
if (this.#state !== PENDING)
return
this.#reason = reason
this.#state = REJECTED
let len = this.#rejectQueue.length
while(len--) {
loop(() => {
const [promise2, onRejected, promise2State] = this.#rejectQueue.shift()
if (isFunc(onRejected)) {
try {
const x = (1, onRejected)(this.#reason)
this.#Resolve(promise2, x)
} catch(e) {
promise2State.rej(e)
}
} else {
promise2State.rej(this.#reason)
}
})
}
}
//...
}
- 最后是
Resolve
部分代码
class $Promise {
//...
#Resolve(promise, x, promiseState){
if (promise === x) {
const reason = new TypeError()
promiseState.rej(reason)
return
}
if (x instanceof $Promise) {
x.then(promiseState.res, promiseState.rej)
} else if(isFunc(x) || isObject(x)) {
let then = null
try {
then = x.then
} catch(e) {
promiseState.rej(e)
}
if (isFunc(then)) {
let settled = false
try {
then.apply(
x,
[y => {
if (!settled) {
settled = true
this.#Resolve(promise, y, promiseState)
}
},
r => {
if (!settled) {
settled = true
this.#Resolve(promise, r, promiseState)
}
}]
)
} catch (e) {
promiseState.rej(e)
}
} else {
promiseState.res(x)
}
} else {
promiseState.res(x)
}
}
//...
}
如果真的按照标准尝试去实现一个 Promise
是一定会遇到下面的问题的:
then
方法接收的参数的调用时机是什么时候:是在对应的Promise
的状态已兑现且仅剩与平台相关的代码时调用,简单点说就是state
的值已经改变且前一个微任务
执行完毕之后。虽然是用宏任务
实现的。- 改变
Promise
状态的途径有哪些:这个问题涉及多个Promise
。第一个是显示创建的,第二个是在代码内部隐式创建的,第三个是作为返回值返回的。虽然有很多Promise
需要处理,但是只需要记住的是,只有通过reject
,resovle
两种方式才能改变 对应的状态。 - 如何处理关联的
Promise
状态:其实此次用例没有过完的主要原因就是没有处理好Promise
之间的关联状态。Promise
存在一个已解决未兑现的中间状态,就是用一个Promise
最为返回值,此时两个Promise
的状态是相关联的,而且这个关联出于一个递归的状态。 - 错误状态是如何展示的:
Promise
的机制是不会再当前结果既处理结果又错误的,所以通常捕获此次执行过程中的错误状态都是通过一个隐式生成的Promise
来反馈的
以上几点就是我在实现过程中遇到的需要仔细思考的问题,过段时间会尽可能的补全和解决当前的问题,然后详细的记录一下实现过程