系列文章目录
根据PromiseA+规范,自己手动实现一个Promise
前言
在上一篇文章中,我们已经实现了MyPromise关于then方法的功能实现,本篇文章实现catch、finally、resolve、reject和all这五个方法,这些方法在Promise A+规范中并没有提及,而是由ES6提出并实现的,因此本篇文章参考了MDN的相关文档,具体链接放在了最后。
一、catch方法
MDN关于catch方法的描述是它和then方法一样,只是专门用于处理失败的回调,因此最简单的方法就是在catch方法中再返回then方法,把接收的回调作为then方法的onRejected回调函数传入即可
catch(onRejected) {
return this.then(undefined, onRejected)
}
执行试一试
运行结果没有问题
二、finally方法
MDN关于finally方法的描述是:finally方法类似于调用 then(onFinally, onFinally),无论Promise执行的是成功还是失败,都需要执行onFinally方法,同时onFinally 回调函数不接收任何参数,finally() 调用通常是透明的,不会更改原始 promise 的状态。因此实现思路就是在finally函数中还是返回then方法,但无论成功还是失败都调用onFinally,之后如果是成功则返回成功的结果,失败则抛出失败的原因,实现和then方法中一样的状态穿透效果。
finally(onFinally) {
return this.then((data) => {
onFinally()
return data
}, (error) => {
onFinally()
throw error
})
}
执行试一试
运行结果没有问题
三、resolve方法
MDN关于resolve方法的描述:resolve方法是一个静态方法,接收一个参数value,用于直接将给定的值转换为一个 Promise。而根据参数value传入的不同又分为几种情况
- 如果value是一个Promise对象,则直接返回
- 如果value是一个thenable(这个thenable就是上一篇文章中提到过的满足Promise A+规范的Promise:即实现了then方法东西,这里可以借助上一篇文章中就已经实现的isPromiseLike函数进行判断),调用这个thenable的then方法,将Promise实例的resolve和reject作为参数传入
- 如果value不是以上任何情况,调用resolve方法,把value作为resolve的参数
最后返回Promise的实例
static resolve(value) {
if (value instanceof MyPromise) return value // 如果是一个MyPromise实例
let _resolve, _reject;
const p = new MyPromise((resolve, reject) => { // 创建一个实例
_resolve = resolve
_reject = reject
})
if (p.#isPromiseLike(value)) { // 如果是一个thenable
value.then(_resolve, _reject) // 调用value的then方法,把保存下来的resolve和reject方法作为参数传入
} else {
_resolve(value) // 说明什么都不是,那直接返回一个MyPromise实例
}
return p
}
测试一下,首先是value是MyPromise的情况
通过MyPromise返回的就是传入的value,没有问题
然后是传入thenable的情况
结果也没用问题,最后则是随便传入其他数据
运行结果没有问题
这里有一个注意点,当第二种情况时,因为我们要实现的resolve是一个静态方法,而MyPromise实例的resolve、reject和比较函数isPromiseLike都是实例方法,静态方法不能直接调用实例方法,因此这里的解决方案就是先提前创建两个变量_resolve和_reject,再创建一个MyPromise的实例,用直接创建的变量保存实例中的resolve与reject方法;而isPromiseLike调用时则是通过实例调用。
四、reject方法
MDN关于reject方法的描述是:Promise.reject 静态方法返回一个被拒绝的 Promise 对象。通过使用 Error 的实例获取错误原因 reason 。因此实现起来很简单,直接返回一个Promise实例,调用实例的reject方法,把传递value作为的错误原因传入即可。
static reject(value) {
return new MyPromise((resolve, reject) => {
reject(value)
})
}
五、all方法
MDN关于all方法描述:Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。
all方法会接收一个可迭代的对象,因此这里只能用for…of进行循环判断当前传入的具体数量,定为count,如果count创建一个MyPromise实例,与resolve方法类似,将resolve与reject方法提前存起来,然后判断可迭代对象是否是0,如果是0,直接调用resove,把最后的结果传入,否则变量这个可迭代对象,先将每一项都通过MyPromise.resolve变成一个MyPromise实例,再调用then方法,因为如果一个MyPromise实例失败则整体失败,因此then方法中的onReject方法位置直接传入之前保存的_reject即可,而onFulFilled则需要将当前回调的传入结果作为参数加入到结果数组result的指定位置,最后判断当完成的MyPromise数量等于传入的可迭代数量,调用保存的_resolve方法并把结果数组作为参数传入。最后返回MyPromise实例。
static all(proms) {
let _resolve, _reject;
const p = new MyPromise((resolve, reject) => {
_resolve = resolve
_reject = reject
})
let count = 0; // 传入的可迭代的数量
let result = []; // 最后的结果
let fulfilledCount = 0; // 完成的MyPromise的数量
for (const prom of proms) {
const i = count
count++
MyPromise.resolve(prom).then((data) => {
// 将成功的数据汇总到 result
result[i] = data
fulfilledCount++
// 判断是不是全部完成
if (fulfilledCount === count) {
return _resolve(result)
}
},_reject)
}
if (count===0) {
_resolve(result)
}
return p
}
测试一下
结果没有问题。
至此,catch、finally、resolve、reject和all方法全部完成
总结
本篇文章,完成了ES6中关于Promise的catch方法、finally方法、resolve方法、reject方法和all方法的实现。最后,附上MyPromise的完整代码
const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'
class MyPromise {
#state = PENDING
#result = undefined
#handler = []
// 判断是否是符合Promise A+规范的promise
#isPromiseLike(value) {
if (value && (typeof value === 'object' || typeof value === 'function')) {
return typeof value.then === 'function'
}
return false
}
// 将任务放入异步队列里
#runMicroTask(func) {
if (typeof process === 'object' && typeof process.nextTick === 'function') { // 模拟node环境微队列执行
process.nextTick(func)
} else if (typeof MutationObserver === 'function') { // 模拟浏览器环境的微队列执行
const ob = new MutationObserver(func) // MutationObserver是一个观察器,它会自动将一个回调函数放入微队列
const textNode = document.createTextNode('1') // 构建一个文本节点用于观察器观察
ob.observe(textNode, { // 指定观察器观察的节点
characterData: true // 观察字符的变化
})
textNode.data = '2' // 手动修改文本节点,当被观察的对象发生变化时,观察器会把回调放入微队列执行,从而实现模拟微队列
} else {
setTimeout(func, 0) // 既不是node环境也不是浏览器,只能用setTimeout来实现异步了
}
}
#runOne(callback, resolve, reject) { // callback 回调函数(onFulfilled||onRejected)
this.#runMicroTask(() => { // 将之前的逻辑通过一个箭头函数的形式放入runMicroTask实现模拟放入微队列
if (typeof callback !== 'function') { // 判断传入的回调函数是否是函数,不是则走穿透逻辑
const settled = this.#state === FULFILLED ? resolve : reject // 定义一个变量settled,根据当前的state判断此次应该执行什么函数
settled(this.#result)
return; // 确定不是函数,后面的逻辑也不会再走了
}
try {
const data = callback(this.#result)
if (this.#isPromiseLike(data)) { // 回调函数正常执行且回调函数的返回结果是个Promise,需要调用返回结果的then方法,并把resolve和reject传进去,由返回结果的Promise判断应该执行哪个
data.then(resolve, reject)
} else {
resolve(data) // 回调函数正常执行且回调函数的返回结果不是个Promise,执行resolve
}
} catch (error) {
reject(error) // 捕获到了回调函数执行时抛出的错误,此时无论state是什么都会执行reject
}
})
}
#run() {
if (this.#state === PENDING) return
while (this.#handler.length) {
const { onFulfilled, onRejected, resolve, reject } = this.#handler.shift()
if (this.#state === FULFILLED) {
this.#runOne(onFulfilled, resolve, reject) // 成功传递onFulfilled
} else if (this.#state === REJECTED) {
this.#runOne(onRejected, resolve, reject)// 失败传递onRejected
}
}
}
constructor(executor) {
const resolve = (data) => {
this.#changeState(FULFILLED, data)
}
const reject = (reason) => {
this.#changeState(REJECTED, reason)
}
try {
executor(resolve, reject)
} catch (error) {
reject(error)
}
}
#changeState(state, result) {
if (this.#state !== PENDING) return
this.#state = state
this.#result = result
this.#run()
}
then(onFulfilled, onRejected) {
return new MyPromise((resolve, reject) => {
this.#handler.push({
onFulfilled, onRejected, resolve, reject
})
this.#run()
})
}
catch(onRejected) {
return this.then(undefined, onRejected)
}
finally(onFinally) {
return this.then((data) => {
onFinally()
return data
}, (error) => {
onFinally()
throw error
})
}
static resolve(value) {
if (value instanceof MyPromise) return value // 如果是一个MyPromise实例
let _resolve, _reject;
const p = new MyPromise((resolve, reject) => { // 创建一个实例
_resolve = resolve
_reject = reject
})
if (p.#isPromiseLike(value)) { // 如果是一个thenable
value.then(_resolve, _reject) // 调用value的then方法,把保存下来的resolve和reject方法作为参数传入
} else {
_resolve(value) // 说明什么都不是,那直接返回一个MyPromise实例
}
return p
}
static reject(value) {
return new MyPromise((resolve, reject) => {
reject(value)
})
}
static all(proms) {
let _resolve, _reject;
const p = new MyPromise((resolve, reject) => {
_resolve = resolve
_reject = reject
})
let count = 0; // 传入的Promise的数量
let result = []; // 最后的结果
let fulfilledCount = 0; // 完成的Promise的数量
for (const prom of proms) {
const i = count
count++
MyPromise.resolve(prom).then((data) => {
// 将成功的数据汇总到 result
result[i] = data
fulfilledCount++
// 判断是不是全部完成
if (fulfilledCount === count) {
return _resolve(result)
}
},_reject)
}
if (count===0) {
_resolve(result)
}
return p
}
}
MDN文档描述
MDN关于catch方法的文档描述
MDN关于finally方法的文档描述
MDN关于resolve方法的文档描述
MDN关于reject方法的文档描述
MDN关于all方法的文档描述