类似的文章已经数不清了,但我随意挑了几个看看发现要么太复杂,要么甚至是错误的,此外也为了个人学习之用,就手写一个最简单的 promise 。先把几个要实现的关键要点列出来:
- Promise 构造函数是同步执行的,promise.then 中的函数是异步执行的;
- promise .then 可以链式调用,但并不是返回 this,而是返回一个新的 promise 实例;
- promise .then 或者 .catch 的参数期望是函数,传入非函数则会发生值穿透;
- 同一个 promise 实例可以多次 then,每一个 then 都是返回一个新的 promise 实例;
- promise 只有三种状态,状态一经 PENDING -> FULLFILLED 或者 PENDING -> REJECTED 就不会再变化;
- 在 promise 中抛出一个错误就直接 reject;
- then 函数接收的回调函数中 return 一个 promise 的话,就根据这个 promise 的状态来决定 then 函数 return 的状态,回调函数抛出错误的话 then 函数返回的 promise 状态就为 reject,其余情况默认返回状态为 resolve 的 promise,回调函数为 return 的值。
- all 方法返回一个 promise,必须要接收的数组中的 promise 全部 resolve 才会 resolve,有一个 reject 就会 reject
下面将会基于上述几点实现构造函数,实例 then 方法,静态 all 方法。其余的扩展起来就很简单了。
enum STATE {
PENDING,
FULLFILLED,
REJECTED,
}
class MyPromise<T = unknown> {
private state = STATE.PENDING
private data: T | undefined
private resolveCb: (() => void)[] = []
private rejectCb: (() => void)[] = []
static all(myPromises: MyPromise[]) {
return new MyPromise((resolve, reject) => {
let count = 0
const resArr: any[] = []
myPromises.forEach((cb) => {
cb.then(
(val: any) => {
count++
resArr.push(val)
if (count === myPromises.length) {
resolve(resArr)
}
},
(reason: any) => {
reject(reason)
}
)
})
})
}
constructor(
executor: (
resolve: (val?: T) => void,
reject: (reason?: any) => void
) => void
) {
const doResolve = (val?: T) => {
// 状态只能改变一次
if (this.state !== STATE.PENDING) {
return
}
this.state = STATE.FULLFILLED
this.data = val
// 一个 promise 可以接多个 then
setTimeout(() => this.resolveCb.forEach((cb) => cb()))
}
const doReject = (reason: any) => {
if (this.state !== STATE.PENDING) {
return
}
this.state = STATE.REJECTED
this.data = reason
setTimeout(() => this.rejectCb.forEach((cb) => cb()))
}
// 抛出错误就当作 reject
try {
executor(doResolve, doReject)
} catch (e) {
doReject(e)
}
}
then(sucsCb: any, failCb: any) {
// 非函数则值穿透,实际上就是将当前 then 接收到的值(this.data)接着传给下一个 then
if (!isFunction(sucsCb)) {
sucsCb = () => this.data
}
if (!isFunction(failCb)) {
failCb = () => this.data
}
// then 返回的是新的 MyPromise 实例
return new MyPromise((resolve, reject) => {
// 能触发 handle 函数就说明之前构造出来的 MyPromise 实例的状态已经不是 PENDING
// handle 函数中利用闭包找到 resolve/reject 从而改变 then 返回的 MyPromise 实例的状态
const handle = (cb: (val: any) => any) => {
let res
try {
// 由外部传来的回调函数来决定该实例的状态
res = cb(this.data)
if (res instanceof MyPromise) {
res.then(resolve, reject)
} else {
resolve(res)
}
} catch (e) {
// 回调函数抛出错误的话就将 reject
reject(res)
}
}
if (this.state === STATE.PENDING) {
this.resolveCb.push(() => handle(sucsCb))
this.rejectCb.push(() => handle(failCb))
}
else if (this.state === STATE.FULLFILLED) {
// 调用 doResolve 的那一刻就发生了状态变化,这里使用 setTimeout 确保异步
setTimeout(() => handle(sucsCb))
}
else if (this.state === STATE.REJECTED) {
setTimeout(() => handle(failCb))
}
else {
const dummy: never = this.state
}
})
}
}
function isFunction(res: any): res is Function {
return typeof res === 'function'
}