JavaScript|手写Promise

之前的文章里讲了Promise的好处以及具体应用,不了解Promise用法的读者可以移步到这里:JavaScript|Promise使用详解。这里我们主要来看看Promise是怎么实现的。

Promise结构设计

如果要实现一个Promise,应该按照Promise A+规范来设计:Promise A+

const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

class myPromise{
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.reason = undefined

        const resolve = (value) => {
            if (this.status === PROMISE_STATUS_PENDING) { // 设置状态使得其他状态的代码不会执行
                this.status = PROMISE_STATUS_FULFILLED
                console.log('resolve called');
                this.value = value
            }
        }
        const reject = (reason) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_REJECTED
                console.log('reject called');
                this.reason = reason
            }
        }
        executor(resolve, reject) // 调用resolve/reject方法
    }
}

const promise = new myPromise((resolve, reject) => {
    console.log('执行了'); // 执行了
    resolve() // resolve called
    reject() // 要注意,这里不会调用reject方法
})

Promise.then方法设计

Promise.then方法是Promise的核心方法,它的作用是:当Promise的状态变为fulfilled或rejected时,执行then方法的回调函数,但是调用这个方法有需要考虑很多问题,如多次调用,链式调用,以及调用时Promise的状态等等。

then方法基本结构

const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

class myPromise{
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.reason = undefined

        const resolve = (value) => {
            if (this.status === PROMISE_STATUS_PENDING) { // 设置状态使得其他状态的代码不会执行
                this.status = PROMISE_STATUS_FULFILLED
                queueMicrotask(() => { // 异步执行,先执行then方法,将onFulfilled方法放入队列中
                    this.value = value
                    this.onfulfilled&&this.onfulfilled(value)
                })
            }
        }
        const reject = (reason) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                this.status = PROMISE_STATUS_REJECTED
                queueMicrotask(() => {
                    this.reason = reason
                    this.onrejected&&this.onrejected(reason)
                })
            }
        }
        executor(resolve, reject) // 调用resolve/reject方法
    }

    then(onfulfilled, onrejected) {
        this.onfulfilled = onfulfilled
        this.onrejected = onrejected
    }
}

const promise = new myPromise((resolve, reject) => {
    console.log('执行了'); // 执行了
    resolve('res')
}).then(res => {
    console.log(res) // res
}, err => {
    console.log(err)
})

const promise1 = new myPromise((resolve, reject) => {
    console.log('执行了'); // 执行了
    reject('err')
}).then(res => {
    console.log(res)
}, err => {
    console.log(err) // err
})

then方法多次调用

上述实现的then虽然能够实现,但如果再次添加then方法的时候会覆盖掉onfulfilled和onrejected,所以需要添加一个多次调用的方法。这里我们将方法加入到数组中,然后遍历数组,执行每一个方法。

const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

class myPromise{
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.reason = undefined
        this.onfulfilledFns = []
        this.onrejectedFns = []

        const resolve = (value) => {
            if (this.status === PROMISE_STATUS_PENDING) { // 设置状态使得其他状态的代码不会执行
                queueMicrotask(() => { // 异步执行,先执行then方法,将onFulfilled方法放入队列中
                    if (this.status !== PROMISE_STATUS_PENDING) return // 如果状态已经变化,则不执行
                    this.status = PROMISE_STATUS_FULFILLED // 设置状态,放在微任务队列可以保证状态变化的时候,不会执行then方法
                    this.value = value
                    this.onfulfilledFns.forEach(fn => {
                        fn(this.value)
                    })
                })
            }
        }
        const reject = (reason) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return
                    this.status = PROMISE_STATUS_REJECTED
                    this.reason = reason
                    this.onfulfilledFns.forEach(fn => {
                        fn(this.reason)
                    })
                })
            }
        }
        executor(resolve, reject) // 调用resolve/reject方法
    }

    then(onfulfilled, onrejected) {
        // 调用then时,状态确,例如setTimeout(() => {}, 0),那么就会立即执行onfulfilled方法,而不是将方法放入队列中
        if (this.status === PROMISE_STATUS_FULFILLED) {
            onfulfilled(this.value)
        } else if (this.status === PROMISE_STATUS_REJECTED) {
            onrejected(this.reason)
        } 
        // 将成功回调和失败回调放入数组中
        if (this.status === PROMISE_STATUS_PENDING) {
            this.onfulfilledFns.push(onfulfilled)
            this.onrejectedFns.push(onrejected)
        }
    }
}

const promise = new myPromise((resolve, reject) => {
    console.log('执行了'); // 执行了
    resolve('res')
    reject('err')
})

// 调用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)
})

// 在确定Promise状态之后, 再次调用then
setTimeout(() => {
  promise.then(res => {
    console.log("res3:", res)
  }, err => {
    console.log("err3:", err)
  })
}, 1000)

then方法的链式调用

上述实现的then方法多次调用,但如果在一个then后面再次调用then方法,则会报错,原因是then没有返回,我们需要在then函数中添加一个返回值,返回一个新的myPromise对象。

const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

// 抽离try catch函数储存返回值
function execFunctionWithCatchError(execFn, value, resolve, reject) {
    try {
        const reason = execFn(value)
        resolve(reason)   
    } catch (error) {
        reject(error)
    }
}

class myPromise{
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.reason = undefined
        this.onfulfilledFns = []
        this.onrejectedFns = []

        const resolve = (value) => {
            if (this.status === PROMISE_STATUS_PENDING) { // 设置状态使得其他状态的代码不会执行
                queueMicrotask(() => { // 异步执行,先执行then方法,将onFulfilled方法放入队列中
                    if (this.status !== PROMISE_STATUS_PENDING) return // 如果状态已经变化,则不执行
                    this.status = PROMISE_STATUS_FULFILLED // 设置状态,放在微任务队列可以保证状态变化的时候,不会执行then方法
                    this.value = value
                    this.onfulfilledFns.forEach(fn => {
                        fn(this.value)
                    })
                })
            }
        }
        const reject = (reason) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return
                    this.status = PROMISE_STATUS_REJECTED
                    this.reason = reason
                    this.onrejectedFns.forEach(fn => {
                        fn(this.reason)
                    })
                })
            }
        }
        try { // 第一个promise可能报错,报错信息需要通过REJECT方法传递给第二个promise
            executor(resolve, reject) // 调用resolve/reject方法
        } catch (err) {
            reject(err)
        }
    }

    then(onfulfilled, onrejected) {
        return new myPromise((resolve, reject) => { // 直接返回这个promise,如果没有then会直接执行
            // 调用then时,状态确,例如setTimeout(() => {}, 0),那么就会立即执行onfulfilled方法,而不是将方法放入队列中
            if (this.status === PROMISE_STATUS_FULFILLED && resolve) {
                // try { //回调函数需要获取结果或者报错,所以需要try catch
                //     const value = onfulfilled(this.value)
                //     resolve(value)
                // } catch (error) {
                //     reject(error)
                // }
                execFunctionWithCatchError(onfulfilled, this.value, resolve, reject)
            } else if (this.status === PROMISE_STATUS_REJECTED && onrejected) {
                execFunctionWithCatchError(onrejected, this.reason, resolve, reject)
            } 
            // 将成功回调和失败回调放入数组中
            if (this.status === PROMISE_STATUS_PENDING) {
                this.onfulfilledFns.push(() => {
                    execFunctionWithCatchError(onfulfilled, this.value, resolve, reject)
                })
                this.onrejectedFns.push(() => {
                    execFunctionWithCatchError(onrejected, this.reason, resolve, reject)
                })
            }
        })
    }
}

const promise = new myPromise((resolve, reject) => {
    console.log('执行了'); // 执行了
    resolve('res')
    reject('err')
})

// 调用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)
})

Promise.catch方法设计

Promise.catch方法是Promise.prototype.then方法的简化版本,当Promise的状态变为rejected时,会调用参数函数,并将Promise的reason作为参数传入函数。在这里可以直接调用then方法来实现吗,除了调用then,还需要设置相关的边界条件处理undefined,并且要确保resolve和reject方法的返回值能够被下一个方法捕获到。

const PROMISE_STATUS_PENDING = 'pending'
const PROMISE_STATUS_FULFILLED = 'fulfilled'
const PROMISE_STATUS_REJECTED = 'rejected'

// 抽离try catch函数储存返回值
function execFunctionWithCatchError(execFn, value, resolve, reject) {
    try {
        const reason = execFn(value)
        resolve(reason)   
    } catch (error) {
        reject(error)
    }
}

class myPromise{
    constructor(executor) {
        this.status = PROMISE_STATUS_PENDING
        this.value = undefined
        this.reason = undefined
        this.onfulfilledFns = []
        this.onrejectedFns = []

        const resolve = (value) => {
            if (this.status === PROMISE_STATUS_PENDING) { // 设置状态使得其他状态的代码不会执行
                queueMicrotask(() => { // 异步执行,先执行then方法,将onFulfilled方法放入队列中
                    if (this.status !== PROMISE_STATUS_PENDING) return // 如果状态已经变化,则不执行
                    this.status = PROMISE_STATUS_FULFILLED // 设置状态,放在微任务队列可以保证状态变化的时候,不会执行then方法
                    this.value = value
                    this.onfulfilledFns.forEach(fn => {
                        fn(this.value)
                    })
                })
            }
        }
        const reject = (reason) => {
            if (this.status === PROMISE_STATUS_PENDING) {
                queueMicrotask(() => {
                    if (this.status !== PROMISE_STATUS_PENDING) return
                    this.status = PROMISE_STATUS_REJECTED
                    this.reason = reason
                    this.onrejectedFns.forEach(fn => {
                        fn(this.reason)
                    })
                })
            }
        }
        try { // 第一个promise可能报错,报错信息需要通过REJECT方法传递给第二个promise
            executor(resolve, reject) // 调用resolve/reject方法
        } catch (err) {
            reject(err)
        }
    }

    then(onfulfilled, onrejected) {
        // catch 传入 undefined 会抛出异常,让 promise 去调用下一个 reject
        const defaultOnRejected = err => {throw err}
        onrejected = onrejected || defaultOnRejected
        // resolve 也需要返回
        const defaultOnFulfilled = value => { value }
        onfulfilled = onfulfilled || defaultOnFulfilled

        return new myPromise((resolve, reject) => { // 直接返回这个promise,如果没有then会直接执行
            // 调用then时,状态确,例如setTimeout(() => {}, 0),那么就会立即执行onfulfilled方法,而不是将方法放入队列中
            if (this.status === PROMISE_STATUS_FULFILLED && onfulfilled) {
                //回调函数需要获取结果或者报错,所以需要try catch
                execFunctionWithCatchError(onfulfilled, this.value, resolve, reject)
            } 
            if (this.status === PROMISE_STATUS_REJECTED && onrejected) {
                execFunctionWithCatchError(onrejected, this.reason, resolve, reject)
            } 
            // 将成功回调和失败回调放入数组中
            if (this.status === PROMISE_STATUS_PENDING) {
                // catch 会传入 undefined
                if (onfulfilled) this.onfulfilledFns.push(() => {
                    execFunctionWithCatchError(onfulfilled, this.value, resolve, reject)
                })
                if (onrejected) this.onrejectedFns.push(() => {
                    execFunctionWithCatchError(onrejected, this.reason, resolve, reject)
                })
            }
        })
    }

    catch(onrejected) {
        return this.then(undefined, onrejected) // 将实例返回出来,以便下一个方法调用
    }
}

const promise = new myPromise((resolve, reject) => {
    console.log('执行了'); // 执行了
    // resolve('res')
    reject('err')
})

// 调用catch方法
promise.then(res => {
  console.log("res:", res)
}).catch(err => {
  console.log("err:", err) // err: err
})

Promise.finally方法设计

Promise.finally的实现可以直接在将then方法下的两个参数传入finally的方法实现:

class myPromise{
    //...其余方法同上,注意catch方法
    finally(onFinally) {
        this.then(() => onFinally(), () => onFinally())
    }
}

const promise = new myPromise((resolve, reject) => {
    console.log('执行了'); // 执行了
    resolve('res')
}).then((res) => {
    console.log("res:", res) // res: res
}).catch((err) => {
    console.log("err:", err)
}).finally(() => {
    console.log("finally") // finally
})

Promise.resolve && Promise.reject方法直接实现

通过设置静态函数来 new 一个新的 promise 就可以了:

class myPromise{
    //...其余方法同上
    static resolve(value) {
        return new myPromise((resolve, reject) => {
            resolve(value)
        })
    }
    static reject(reason) {
        return new myPromise((resolve, reject) => {
            reject(reason)
        })
    }
}

myPromise.resolve('Hello World').then(res => {
    console.log("res:", res) // res: Hello World
})

myPromise.reject('err').catch(err => {
    console.log("err:", err) // err: err
})

Promise.all && Promise.allSettled方法实现

两个方法都是通过对数组进行遍历,分别执行Promise,但不同的是:Promise.allSettled返回的是一个数组,数组中的每一项是一个Promise的状态,而Promise.all返回的是一个Promise,Promise.all返回的Promise的状态是数组中所有Promise的状态的并集。

class myPromise {
    // ...其余方法同上
    static all(promises) {
        return new myPromise((resolve, reject) => {
            let result = []
            promises.forEach(promise => {
                promise.then(
                res => {
                    result.push(res)
                    // 如果数组中的所有Promise都执行完毕,则执行resolve
                    if (result.length === promises.length) {
                        resolve(result)
                    }
                }),
                // 如果数组中的某一个Promise执行失败,则执行reject
                err => {
                    reject(err)
                })
            })
        })
    }

    static allSettled(promises) {
        return new myPromise((resolve, reject) => {
            let result = []
            promises.forEach(promise => {
                // 对每个结果进行分类,分为fulfilled和rejected,加入到数组中
                promise.then(
                res => {
                    result.push({status: 'fulfilled', value: res})
                    if (result.length === promises.length) {
                        resolve(result)
                    }
                }),
                err => {
                    result.push({status: 'rejected', reason: err})
                    if (result.length === promises.length) {
                        resolve(result)
                    }
                })
            })
        })
    }
}

const p1 = new myPromise((resolve) => {
  setTimeout(() => { resolve(1111) }, 1000)
})
const p2 = new myPromise((resolve, reject) => {
  setTimeout(() => { reject(2222) }, 2000)
})
const p3 = new myPromise((resolve) => {
  setTimeout(() => { resolve(3333) }, 3000)
})

myPromise.all([p1, p2, p3]).then(res => {
  console.log("res:", res) // res: [ 1111, 2222, 3333 ]
})

myPromise.allSettled([p1, p2, p3]).then(res => {
  console.log("res:", res) // res: [ { status: 'fulfilled', value: 1111 }, { status: 'rejected', reason: 2222 }, { status: 'fulfilled', value: 3333 } ]
})

Promise.race && Promise.any 方法设计

两个方法都是直接执行第一个可以执行Promise,区别在于:race执行到第一个就可以返回,any则需要等待一个resolve才可以返回,否则返回reject:

class myPromise {
    // ...同上
    static race(promises) {
        return new myPromise((resolve, reject) => {
            promises.forEach(promise => {
                // promise.then(res => {
                //   resolve(res)
                // }, 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(resolve, err => {
                    // 如果有一个Promise执行成功,则执行resolve
                    reasons.push(err)
                    if (reasons.length === promises.length) {
                        reject(new AggregateError(reasons))
                    }
                })
            })
        })
    }
}

const p1 = new myPromise((resolve) => {
  setTimeout(() => { resolve(1111) }, 1000)
})

const p2 = new myPromise((resolve, reject) => {
  setTimeout(() => { reject(2222) }, 2000)
})

const p3 = new myPromise((resolve) => {
  setTimeout(() => { resolve(3333) }, 3000)
}).

myPromise.race([p1, p2, p3]).then(res => {
    console.log(res); // 111
})

myPromise.any([p1, p2, p3]).then(res => {
    console.log(res); // 111
})