概述
哈哈哈,XDM,不好意思啦!有【亿】点点标题党啦。手写整个 Promise 不太可能,但手写 Promise 的部分内容确实是我们经常会遇到,比如手写 Promise.all、Promise.race 等等吧。那为什么面试官喜欢考这些呢?因为 Promise 实在是太重要了,ES6之后,想想我们但凡涉及到异步相关的封装是不是首先想到的就是 Promise!考察 Promise, 不但考察了我们本身对于 Promise 的理解程度,也考察了我们的 JS 功底。
好了,闲言少叙,让我们开始吧!
概念
关于 Promise 的具体概念,这里就不过多叙述了,可以参考 MDN 中的叙述
我们来看一下Promise的API是怎么样的:
- Promise是一个类,可以翻译成 承诺、许诺 、期约;
- 当我们需要给予调用者一个承诺:待会儿我会给你回调数据时,就可以创建一个 Promise 的对象;
- 在通过 new 创建 Promise 对象时,我们需要传入一个回调函数,我们称之为 executor
- 这个回调函数会被立即执行,并且给传入另外两个回调函数 resolve、reject;
- 当我们调用 resolve 回调函数时,会执行 Promise 对象的 then 方法传入的回调函数;
- 当我们调用 reject 回调函数时,会执行 Promise 对象的 catch 方法传入的回调函数;
const promise = new Promise((resolve, reject) => {
// 调用resolve, 那么then传入的回调会被执行
resolve("哈哈哈")
// 调用reject, 那么catch传入的回调会被执行
reject("错误信息")
})
promise.then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
上面Promise使用过程,我们可以将它划分成三个状态:
- 待定(pending): 初始状态,既没有被兑现,也没有被拒绝;
- 当执行 executor 中的代码时,处于该状态;
- 已兑现(fulfilled): 意味着操作成功完成;
- 执行了 resolve 时,处于该状态;
- 已拒绝(rejected): 意味着操作失败;
- 执行了 reject 时,处于该状态;
Promise 有且只有这三种状态,且一旦由 pending 状态转变为 fulfilled 转态或者 rejected 转态,状态就固定不变了,不能够逆转!
接下来我们开始来手写 Promise 吧!不要害怕,我会一步步的带你们手写出来,几乎每行代码都会有注释,整个 Promise 的代码加上注释也不过两百行,跟着我,这一次你一定能把 Promise 拿下!
Promise 成员方法
1. 结构设计:定义 MyPromise 构造函数,并实现基本的状态属性
通过上面的 [概念] 环节相信大家已经知道了 Promise 在实例化的时候会传入一个回调函数 executor,并且立即执行。这个回调函数会接收(之所以用“会接收”而没有用“必须要”,是因为也可以传一个参数进去,原因看到后面你就懂啦~)两个参数 — resolve 和 reject,这两个参数也是以回调函数的形式传入,并且先执行哪一个过后,后面的另一个就不会再执行了,因为先执行的已经将 Promise 的状态锁死了。
这里要做一点说明,事实上,executor 的两个接收的两个回调函数参数 — resolve 和 reject,只要你在内部调用了就一定都会执行。之所以表现为一个执行了,另一个未执行,是因为代码在内部做了限制,所以是内部的代码未执行,而不是回调函数未执行。
举个简单的例子:
function foo() {
if(false) { console.log(“111”) }
}
foo()
调用 foo 函数,foo 执行了吗?肯定执行了,但永远都不会输出 “111”
下面我们一起来看一下代码吧!
class MyPromise {
// 定义 Promise 的三种状态
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
// 初始化状态为pending
this.status = MyPromise.PENDING
// 初始化成功的值为undefined
this.value = undefined
// 初始化失败的原因为undefined
this.reason = undefined
// 定义resolve方法
const resolve = (value) => {
// 只有在pending状态才能更改状态和值
if(this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED
this.value = value
console.log("resolve被调用---", value)
}
}
// 定义reject方法
const reject = (reason) => {
// 只有在pending状态才能更改状态和原因
if(this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED
this.reason = reason
console.log("reject被调用---", reason)
}
}
// 立即执行执行器函数
try {
executor(resolve, reject)
} catch (error) {
// 如果执行器函数抛出异常,将Promise状态更改为rejected
reject(error)
}
}
}
我们来测试一下
const promise = new MyPromise((resolve, reject) => {
// resolve("11") // resolve被调用--- 11
// reject("22") // reject被调用--- 22
throw Error("errorrrrrrrr") // reject被调用--- errorrrrrrrr
})
2. then 方法设计(书接上回,不同的地方就用 —222— 标注吧)
then 方法是整个 Promise 设计中最重要部分,当然,也是最难的部分,我们会分三个小节将其打通。将这个方法掌握以后,后面的就会迎刃而解了。
我们知道 then 方法接受两个参数:
- fulfilled 的回调函数:当状态变成 fulfilled 时会回调的函数;
- rejected 的回调函数:当状态变成 rejected 时会回调的函数;
所以 then 函数差不多长这个样子
then(onFulfilled, onRejected) {
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
我们还知道 then 方法里面的代码应该是要被当成微任务进行执行的,所以我们还要创造出这个环境。这里我们使用 queueMicrotask 进行实现。
现在我们一起来看一下整体的代码吧!
class MyPromise {
// 定义 Promise 的三种状态
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
// 初始化状态为pending
this.status = MyPromise.PENDING
// 初始化成功的值为undefined
this.value = undefined
// 初始化失败的原因为undefined
this.reason = undefined
// 定义resolve方法
const resolve = (value) => {
// 只有在pending状态才能更改状态和值
if(this.status === MyPromise.PENDING) {
this.status = MyPromise.FULFILLED
// ---222---
queueMicrotask(() => {
this.value = value
this.onFulfilled(this.value)
})
}
}
// 定义reject方法
const reject = (reason) => {
// 只有在pending状态才能更改状态和原因
if(this.status === MyPromise.PENDING) {
this.status = MyPromise.REJECTED
// ---222---
queueMicrotask(() => {
this.reason = reason
this.onRejected(this.reason)
})
}
}
// 立即执行执行器函数
try {
executor(resolve, reject)
} catch (error) {
// 如果执行器函数抛出异常,将Promise状态更改为rejected
reject(error)
}
}
// ---222---
// then方法接受两个参数:
// fulfilled 的回调函数:当状态变成 fulfilled 时会回调的函数;
// reject 的回调函数:当状态变成 rejected 时会回调的函数;
then(onFulfilled, onRejected) {
this.onFulfilled = onFulfilled
this.onRejected = onRejected
}
}
我们来测试一下:
const promise = new MyPromise((resolve, reject) => {
console.log("状态pending")
// reject(2222)
resolve(1111)
})
// 调用then方法
promise.then(res => {
console.log("res1:", res)
return 1111
}, err => {
console.log("err:", err)
})
// 输出:
// 状态pending
// res1: 1111
3. then 方法优化1(书接上回,不同的地方就用 —333— 标注吧)
不知道大家有没有发现上述代码中的问题:
- 首先,上述代码在 promise 实例化过后,如果紧接着不调用 then 方法,是会报错的,也就是说实例化和 then 方法的调用捆绑了
- promise 的 then 方法是可以多次调用的,且每个回调函数都会被执行(也就是 then 里面的 onFulfilled 或 onRejected,注意是 或,不是 和),而上次代码调用多次 then 方法后,只会执行最后一个,因为后面的会将前面的覆盖掉,我们必须使用两个队列将其存储起来,待到合适的时机(也就是 Promise 的状态非 pending 时)去执行。
接下来我们就针对这两个问题进行一下优化吧!
闲言少叙,看代码吧!
class MyPromise {
// 定义 Promise 的三种状态
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
// 初始化状态为pending
this.status = MyPromise.PENDING
// 初始化成功的值为undefined
this.value = undefined
// 初始化失败的原因为undefined
this.reason = undefined
// ---333---
// 初始化成功处理函数队列
this.onFulfilledFns = []
// 初始化失败处理函数队列
this.onRejectedFns = []
// 定义resolve方法
const resolve = (value) => {
// 只有在pending状态才能更改状态和值
if(this.status === MyPromise.PENDING) {
// ---222---
// 添加微任务
queueMicrotask(() => {
// ---333---
if(this.status !== MyPromise.PENDING) return
this.status = MyPromise.FULFILLED
this.value = value
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
// this.onFulfilled(this.value)
// console.log("resolve被调用---", value)
})
}
}
// 定义reject方法
const reject = (reason) => {
// 只有在pending状态才能更改状态和原因
if(this.status === MyPromise.PENDING) {
// ---222---
// 添加微任务
queueMicrotask(() => {
// ---333---
if(this.status !== MyPromise.PENDING) return
this.status = MyPromise.FULFILLED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
// this.onRejected(this.reason)
// console.log("reject被调用---", reason)
})
}
}
// 立即执行执行器函数
try {
executor(resolve, reject)
} catch (error) {
// 如果执行器函数抛出异常,将Promise状态更改为rejected
reject(error)
}
}
// ---222---
// then方法接受两个参数:
// fulfilled 的回调函数:当状态变成 fulfilled 时会回调的函数;
// reject 的回调函数:当状态变成 rejected 时会回调的函数;
then(onFulfilled, onRejected) {
// ---333---
// 1.如果在then调用的时候, 状态已经确定下来
if(this.status === MyPromise.FULFILLED && onFulfilled) {
onFulfilled(this.value)
}
if(this.status === MyPromise.REJECTED && onRejected) {
onRejected(this.reason)
}
// 2.如果在then调用的时候, 状态未确定下来,
// 则将成功回调和失败的回调放到对应的数组中,
// 等到状态确定时从对应数组中依次取出执行(队列)
if(this.status === MyPromise.PENDING) {
this.onFulfilledFns.push(onFulfilled)
this.onRejectedFns.push(onRejected)
}
}
}
我们来测试一下:
const promise = new MyPromise((resolve, reject) => {
console.log("状态pending")
resolve(1111) // resolved/fulfilled
reject(2222)
})
// 调用then方法多次调用
promise.then(res => {
console.log("res1:", res)
}, err => {
console.log("err:", err)
})
promise.then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
// 输出:
// 状态pending
// res1: 1111
// res2: 1111
// 此时 promise 形如
// MyPromise {
// status: 'fulfilled', value: 1111, reason: undefined,
// onFulfilledFns: Array(2), onRejectedFns: Array(2)
// }
4. then 方法优化2(书接上回,不同的地方就用 —444— 标注吧)
经过上述的优化以后,我们的 then 函数已经表现的不错了,但是,大家有没有发现,其实上述代码中还是有一个问题 — promise 是支持链式调用的,也就是可以不断的 .then
但是我们写的代码目前还不支持,那要怎么让它支持链式调用呢? 我们可以将 then 方法整个用一个新的 Promise 包裹,然后返回这个新的 Promise 实例,不就实现了嘛! 对吧, 彦祖亦菲们!~
我们一起来看一下代码吧
class MyPromise {
// 定义 Promise 的三种状态
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
// 初始化状态为pending
this.status = MyPromise.PENDING
// 初始化成功的值为undefined
this.value = undefined
// 初始化失败的原因为undefined
this.reason = undefined
// ---333---
// 初始化成功处理函数队列
this.onFulfilledFns = []
// 初始化失败处理函数队列
this.onRejectedFns = []
// 定义resolve方法
const resolve = (value) => {
// 只有在pending状态才能更改状态和值
if(this.status === MyPromise.PENDING) {
// ---222---
// 添加微任务
queueMicrotask(() => {
// ---333---
if(this.status !== MyPromise.PENDING) return
this.status = MyPromise.FULFILLED
this.value = value
// ---333---
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
// this.onFulfilled(this.value)
// console.log("resolve被调用---", value)
})
}
}
// 定义reject方法
const reject = (reason) => {
// 只有在pending状态才能更改状态和原因
if(this.status === MyPromise.PENDING) {
// ---222---
// 添加微任务
queueMicrotask(() => {
// ---333---
if(this.status !== MyPromise.PENDING) return
this.status = MyPromise.FULFILLED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
// this.onRejected(this.reason)
// console.log("reject被调用---", reason)
})
}
}
// 立即执行执行器函数
try {
executor(resolve, reject)
} catch (error) {
// 如果执行器函数抛出异常,将Promise状态更改为rejected
reject(error)
}
}
// ---222---
// then方法接受两个参数:
// fulfilled 的回调函数:当状态变成 fulfilled 时会回调的函数;
// reject 的回调函数:当状态变成 rejected 时会回调的函数;
then(onFulfilled, onRejected) {
// ---444---
return new MyPromise((resolve, reject) => {
// ---333---
// 1.如果在then调用的时候, 状态已经确定下来
if(this.status === MyPromise.FULFILLED && onFulfilled) {
// try {
// const value = onFulfilled(this.value)
// resolve(value)
// } catch (error) {
// reject(error)
// }
this.#execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if(this.status === MyPromise.REJECTED && onRejected) {
// try {
// const value = onRejected(this.reason)
// resolve(value)
// } catch (error) {
// reject(error)
// }
this.#execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2.如果在then调用的时候, 状态未确定下来,
// 则将成功回调和失败的回调放到对应的数组中,
// 等到状态确定时从对应数组中依次取出执行(队列)
if(this.status === MyPromise.PENDING) {
this.onFulfilledFns.push(() => {
// try {
// const value = onFulfilled(this.value)
// resolve(value)
// } catch(err) {
// reject(err)
// }
this.#execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
this.onRejectedFns.push(() => {
// try {
// const reason = onRejected(this.reason)
// resolve(reason)
// } catch(err) {
// reject(err)
// }
this.#execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
})
}
// ---444---
// 定义一个私有方法进行统一执行处理(工具函数)
#execFunctionWithCatchError(execFn, value, resolve, reject) {
try {
const result = execFn(value)
resolve(result)
} catch(err) {
reject(err)
}
}
}
我们来测试一下
const promise = new MyPromise((resolve, reject) => {
console.log("状态pending")
// resolve(1111) // resolved/fulfilled
reject(2222)
// throw new Error("executor error message")
})
// 调用then方法多次调用
promise.then(res => {
console.log("res1:", res)
return "aaaa"
// throw new Error("err message")
}, err => {
console.log("err1:", err)
return "bbbbb"
// throw new Error("err message")
}).then(res => {
console.log("res2:", res)
}, err => {
console.log("err2:", err)
})
// 输出:
// 状态pending
// err1: 2222
// res2: bbbbb
5. catch 方法设计(书接上回,不同的地方就用 —555— 表示吧)
能坚持到这里的你真棒!!! 哈哈哈,我们已经将最难的部分跨过去了,相信把 then 方法啃下来的你,接下来的部分对你来说一定 SO EASY!
catch 和 下面的 finally 方法都是 then 方法的语法糖,我们就不过多叙述了,直接看代码吧!
// ---555---
catch(onRejected) {
this.then(undefined, onRejected)
}
测试代码:
const promise = new MyPromise((resolve, reject) => {
console.log("状态pending")
// resolve(1111) // resolved/fulfilled
reject(2222)
})
// 调用then方法多次调用
promise.then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
大家自己跑跑代码看看吧,就不过多叙述了,紧接着,我们就来看一下 finally 方法吧!
6. finally 方法设计(书接上回,不同的地方就用 —666— 表示吧)
闲言少叙,直接看代码吧
// ---666---
// finally 的回调函数不接收参数(传了也没用)
finally(onFinally) {
this.then(() => {
onFinally()
}, () => {
onFinally()
})
}
测试代码:
const promise = new MyPromise((resolve, reject) => {
console.log("状态pending")
resolve(1111) // resolved/fulfilled
// reject(2222)
})
// 调用then方法多次调用
promise.then(res => {
console.log("res1:", res)
return "aaaaa"
}).then(res => {
console.log("res2:", res)
}).catch(err => {
console.log("err:", err)
}).finally(() => {
console.log("finally")
})
至此,Promise 的成员(实例)方法就全都实现完了,接着来实现类方法吧
Promise 类方法
7. 类方法设计之 resolve - reject(书接上回,不同的地方就用 —777— 表示吧)
这两个方法都是 构造器 constructor 方法执行参数 executor 的语法糖
废话不多说,直接看代码吧
// ---777---
static resolve(value) {
return new MyPromise((resolve) => resolve(value))
}
static reject(reason) {
return new MyPromise((resolve, reject) => reject(reason))
}
测试代码:
MyPromise.resolve("Hello World").then(res => {
console.log("res:", res)
})
MyPromise.reject("Error Message").catch(err => {
console.log("err:", err)
})
接下来我们来看一下 all 和 allSettle 方法吧
8.类方法设计之 all - allSettled(书接上回,不同的地方就用 —888— 表示吧)
all方法:
- 它的作用是将多个 Promise 包裹在一起形成一个新的 Promise;
- 新的 Promise 状态由包裹的所有 Promise 共同决定:
- 当所有的 Promise 状态变成 fulfilled 状态时,新的 Promise 状态为 fulfilled,并且会将所有 Promise 的返回值组成一个数组;
- 当有一个 Promise 状态为 reject 时,新的 Promise 状态为 reject,并且会将第一个 reject 的返回值作为参数;
// ---888---
static all(promises) {
// 问题关键: 什么时候要执行resolve, 什么时候要执行reject
return new MyPromise((resolve, reject) => {
const results = []
promises.forEach(promise => {
promise.then(res => {
results.push(res)
if(results.length === promises.length) {
resolve(results)
}
}).catch(err => {
reject(err)
})
})
})
}
static allSettled(promises) {
return new MyPromise((resolve, reject) => {
const results = []
promises.forEach(promise => {
promise.then(res => {
results.push({status: MyPromise.FULFILLED, value: res })
if (results.length === promises.length) {
resolve(results)
}
}).catch(err => {
results.push({status: MyPromise.REJECTED, reason: err })
if (results.length === promises.length) {
resolve(results)
}
})
})
// 不能将上面分支里面的判断合并成这样,看下面的例子可以知道
// 由于每个promise 的延时不一样,这样设置的话,结果数组就提前返回为空了
// setTimeout(() => {
// resolve(results)
// })
})
}
看一下测试代码:
const p1 = new Promise((resolve) => {
setTimeout(() => { resolve(1111) }, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => { reject(2222) }, 2000)
})
const p3 = new Promise((resolve) => {
setTimeout(() => { resolve(3333) }, 3000)
})
MyPromise.all([p1, p2, p3]).then(res => {
console.log(res)
}).catch(err => {
console.log(err)
})
MyPromise.allSettled([p1, p2, p3]).then(res => {
console.log(res)
})
9.类方法设计之 race - any(书接上回,不同的地方就用 —999— 表示吧)
最后我们来看一下 race 和 any 方法。闲言少叙,我们直接来看代码吧!
- race 是竞技、竞赛的意思,表示多个 Promise 相互竞争,谁先有结果,那么就使用谁的结果;
any 方法是 ES12 中新增的方法,和 race 方法是类似的:
- any 方法会等到一个 fulfilled 状态,才会决定新 Promise 的状态;
- 如果所有的 Promise 都是 reject 的,那么也会等到所有的 Promise 都变成 rejected 状态;
// ---999---
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
// promise.then(res => {
// resolve(res)
// }).catch(err => {
// reject(err)
// })
promise.then(resolve, reject)
})
})
}
static any(promises) {
// resolve必须等到有一个成功的结果
// reject所有的都失败才执行reject
const reasons = []
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(res => {
resolve(res)
}).catch(err => {
reasons.push(err)
if(reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
测试代码:
const p1 = new Promise((resolve, reject) => {
setTimeout(() => { reject(1111) }, 1000)
})
const p2 = new Promise((resolve, reject) => {
setTimeout(() => { reject(2222) }, 2000)
})
const p3 = new Promise((resolve, reject) => {
setTimeout(() => { resolve(3333) }, 3000)
})
MyPromise.race([p1, p2, p3]).then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err)
})
MyPromise.any([p1, p2, p3]).then(res => {
console.log("res:", res)
}).catch(err => {
console.log("err:", err.errors)
})
手写 Promise 的全部代码
class MyPromise {
// 定义 Promise 的三种状态
static PENDING = 'pending'
static FULFILLED = 'fulfilled'
static REJECTED = 'rejected'
constructor(executor) {
// 初始化状态为pending
this.status = MyPromise.PENDING
// 初始化成功的值为undefined
this.value = undefined
// 初始化失败的原因为undefined
this.reason = undefined
// ---333---
// 初始化成功处理函数队列
this.onFulfilledFns = []
// 初始化失败处理函数队列
this.onRejectedFns = []
// 定义resolve方法
const resolve = (value) => {
// 只有在pending状态才能更改状态和值
if(this.status === MyPromise.PENDING) {
// ---222---
// 添加微任务
queueMicrotask(() => {
// ---333---
if(this.status !== MyPromise.PENDING) return
this.status = MyPromise.FULFILLED
this.value = value
// ---333---
this.onFulfilledFns.forEach(fn => {
fn(this.value)
})
// this.onFulfilled(this.value)
// console.log("resolve被调用---", value)
})
}
}
// 定义reject方法
const reject = (reason) => {
// 只有在pending状态才能更改状态和原因
if(this.status === MyPromise.PENDING) {
// ---222---
// 添加微任务
queueMicrotask(() => {
// ---333---
if(this.status !== MyPromise.PENDING) return
this.status = MyPromise.FULFILLED
this.reason = reason
this.onRejectedFns.forEach(fn => {
fn(this.reason)
})
// this.onRejected(this.reason)
// console.log("reject被调用---", reason)
})
}
}
// 立即执行执行器函数
try {
executor(resolve, reject)
} catch (error) {
// 如果执行器函数抛出异常,将Promise状态更改为rejected
reject(error)
}
}
// ---222---
// then方法接受两个参数:
// fulfilled 的回调函数:当状态变成 fulfilled 时会回调的函数;
// reject 的回调函数:当状态变成 rejected 时会回调的函数;
then(onFulfilled, onRejected) {
// ---555---
// 设置一个默认’拒绝‘函数,确保catch调用时不出错
const defaultOnRejected = err => { throw err }
onRejected = onRejected || defaultOnRejected
// ---666---
// 设置一个默认’成功‘函数,确保then调用时不出错
const defaultOnFulfilled = value => { return value }
onFulfilled = onFulfilled || defaultOnFulfilled
// ---444---
return new MyPromise((resolve, reject) => {
// ---333---
// 1.如果在then调用的时候, 状态已经确定下来
if(this.status === MyPromise.FULFILLED && onFulfilled) {
this.#execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
}
if(this.status === MyPromise.REJECTED && onRejected) {
this.#execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
}
// 2.如果在then调用的时候, 状态未确定下来,
// 则将成功回调和失败的回调放到对应的数组中,
// 等到状态确定时从对应数组中依次取出执行(队列)
if(this.status === MyPromise.PENDING) {
this.onFulfilledFns.push(() => {
this.#execFunctionWithCatchError(onFulfilled, this.value, resolve, reject)
})
this.onRejectedFns.push(() => {
this.#execFunctionWithCatchError(onRejected, this.reason, resolve, reject)
})
}
})
}
// ---444---
// 定义一个私有方法进行统一执行处理(工具函数)
#execFunctionWithCatchError(execFn, value, resolve, reject) {
try {
const result = execFn(value)
resolve(result)
} catch(err) {
reject(err)
}
}
// ---555---
catch(onRejected) {
this.then(undefined, onRejected)
}
// ---666---
// finally 的回调函数不接收参数(传了也没用)
finally(onFinally) {
this.then(() => {
onFinally()
}, () => {
onFinally()
})
}
// ---777---
static resolve(value) {
return new MyPromise((resolve) => resolve(value))
}
static reject(reason) {
return new MyPromise((resolve, reject) => reject(reason))
}
// ---888---
static all(promises) {
// 问题关键: 什么时候要执行resolve, 什么时候要执行reject
return new MyPromise((resolve, reject) => {
const results = []
promises.forEach(promise => {
promise.then(res => {
results.push(res)
if(results.length === promises.length) {
resolve(results)
}
}).catch(err => {
reject(err)
})
})
})
}
static allSettled(promises) {
return new MyPromise((resolve, reject) => {
const results = []
promises.forEach(promise => {
promise.then(res => {
results.push({status: MyPromise.FULFILLED, value: res })
if (results.length === promises.length) {
resolve(results)
}
}).catch(err => {
results.push({status: MyPromise.REJECTED, reason: err })
if (results.length === promises.length) {
resolve(results)
}
})
})
})
}
// ---999---
static race(promises) {
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
// promise.then(res => {
// resolve(res)
// }).catch(err => {
// reject(err)
// })
promise.then(resolve, reject)
})
})
}
static any(promises) {
// resolve必须等到有一个成功的结果
// reject所有的都失败才执行reject
const reasons = []
return new MyPromise((resolve, reject) => {
promises.forEach(promise => {
promise.then(res => {
resolve(res)
}).catch(err => {
reasons.push(err)
if(reasons.length === promises.length) {
reject(new AggregateError(reasons))
}
})
})
})
}
}
总结
至此,我们就将手写 Promise 的内容全部完成了,相信大家也发觉了,我们在实现了成员方法 then 以后,后面的进度就推进的特别快,几乎没有什么废话,就是直接上代码,为什么呢?因为我们前面说过,整个Promise 中最重要的也是最难得就是这个 then 方法,把它理解掌握了,后面的方法确实也就能一气呵成了。本人水平有限,文章内容难免出错,如果觉得有问题,还望批评指正~
好了,这次就到这里吧,亦菲彦祖们,我们下次见~
参考内容:
https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Promise
https://mp.weixin.qq.com/s?__biz=Mzg5MDAzNzkwNA==&mid=2247486710&idx=1&sn=faa255d54f78e0297806b077a471e92a&chksm=cfe3ff09f894761f3c1a9bbf85e2cbef1a2b3e4cb6cdbdb040f70b4df63163cd972a2464d649&xtrack=1&scene=0&subscene=244&sessionid=1711941489&clicktime=1711941682&enterid=1711941682&ascene=7&fasttmpl_type=0&fasttmpl_fullversion=7139752-zh_CN-zip&fasttmpl_flag=0&realreporttime=1711941682366&devicetype=android-31&version=2800303b&nettype=3gnet&abtest_cookie=AAACAA%3D%3D&lang=zh_CN&session_us=gh_2c45214772d7&countrycode=CN&exportkey=n_ChQIAhIQQjDXJjXikamFCBdvYk15BhLrAQIE97dBBAEAAAAAAJBWDiJXjDAAAAAOpnltbLcz9gKNyK89dVj0f1aZ1LPwitdZv3s5wfALHbzZ7Tp600VrgOF1hbljxkNTheoVTga0Elvdd7%2B3M%2BZ7RvMxAEHmoMtvU6xM1ABTH1Q2gMQYZKQH3IQJ7VRYxauJbxEh4eQv8EAiypqa4mK%2BWlfl%2FWbcxLH5EF44mt%2FuabuJlaZle2HFaycHj2AzCbs3c%2BSIZbKzEhboqK9gr7vJw%2BqDhS8%2F3UI2q2LGRk26YF7dHHmAaDdb%2FLPpGfBny3wSY1ACD87Dg5bubiHJeV44p5dgZE8%3D&pass_ticket=sT6dXMDD07LNMFn8wiFjN3n%2FPCIO03Oyq6UTk%2FywjC58r2pLlaS9l86Ie%2FJPBl3fV98VB6QUq6hodVUFF3XyYA%3D%3D&wx_header=3
https://mp.weixin.qq.com/s?__biz=Mzg5MDAzNzkwNA==&mid=2247486728&idx=1&sn=3045f09cdd62bb4a0eb2649e77089392&chksm=cfe3fef7f89477e1a5ebfa3fc8c685d184d591dfdf9b64ae5115beb29f1d100d8bb7aad86c3b&xtrack=1&scene=0&subscene=244&sessionid=1711941489&clicktime=1711941519&enterid=1711941519&ascene=7&fasttmpl_type=0&fasttmpl_fullversion=7139752-zh_CN-zip&fasttmpl_flag=0&realreporttime=1711941519749&devicetype=android-31&version=2800303b&nettype=3gnet&abtest_cookie=AAACAA%3D%3D&lang=zh_CN&session_us=gh_2c45214772d7&countrycode=CN&exportkey=n_ChQIAhIQuCssJ0FASZ1BBWpckaLyvBLrAQIE97dBBAEAAAAAAAWBI6W3nYcAAAAOpnltbLcz9gKNyK89dVj0ai9yPa1tosiVKovw48invemLN4FdtNsMcF8CrOJkpAuy1%2BWn71mpoG%2FuGLt3H3I%2Fah3AG35lDIpcpqldKFus9TiFm%2B8TDnT1MaPRzBWd1tcrn%2Ba4lVcIiPrRFM9l9vU%2F0B2%2FUm0bN1JgyIUBKzdw%2BkhuNsq1ULt781CwflOjHjeGy8o%2FqPdFF85LeQGcMC1yTiqWeyzsEePBnsCmV0NfnZ%2B1AklgYgO9YzhktS%2BbGp0SevbXbx%2BM%2Fb%2FROCIA14mR97pqNPM%3D&pass_ticket=sT6dXMDD07LNMFn8wiFjN3n%2FPCIO03Oyq6UTk%2FywjC5eigDJ7ANdHx1joUDdT%2BEbut7M%2F1ZZ51SBvOfAGSVn0g%3D%3D&wx_header=3