JS基础,实现 promise 方法

220 阅读6分钟

一、创建一个类,进行初始化操作

  • 这里需要注意的是,promise 方法包含三种状态:pending、fulfilled、rejected
class MyPromise {
    constructor() {
        this.initValue()
    }

    initValue() {
        this.promiseState = "pending"
        this.promiseResult = null
    }
}

二、添加 resolve 和 reject 方法,并进行 bind 操作

class MyPromise {
    constructor() {
        this.initValue()
        this.initBind()
    }

    initValue() {
        this.promiseState = "pending"
        this.promiseResult = null
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    
    resolve(value) {
    
    }
    
    reject(reason) {
        
    }
}

三、在 constructor 中,传入一个执行参数,执行 promise 的 resolve 和 reject 方法;使用 try catch 捕获 promise 中的 throw,并且执行 reject 方法

class MyPromise {
    constructor(executor) {
        this.initValue()
        this.initBind()

        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    initValue() {
        this.promiseState = "pending"
        this.promiseResult = null
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    
    resolve(value) {
    
    }
    
    reject(reason) {
        
    }
}

四、完善 resolve 和 reject 方法

  • 这里需要注意的是,promise 的三种状态,不可逆向改变
class MyPromise {
    constructor(executor) {
        this.initValue()
        this.initBind()

        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    initValue() {
        this.promiseState = "pending"
        this.promiseResult = null
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    
    resolve(value) {
        if(this.promiseState !== "pending") return

        this.promiseState = "fulfilled"
        this.promiseResult = value
    }
    
    reject(reason) {
        if(this.promiseState !== "pending") return

        this.promiseState = "rejected"
        this.promiseResult = reason
    }
}

五、验证一下

  • 1、实例化 MyPromise 后,传入成功或者失败的回调,状态值是否发生改变
  • 2、在 MyPromise 中 throw,是否会改变状态,并且捕获到异常

image.png

六、实现 then 方法

  • then 方法中传入两个回调函数,成功回调和失败回调
  • 当状态发生改变后,如果是 fulfilled 状态,则执行成功回调,并将结果值传入成功回调
  • 当状态发生改变后,如果是 rejected 状态,则执行失败回调,并将结果值传入失败回调
class MyPromise {
    constructor(executor) {
        this.initValue()
        this.initBind()

        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    initValue() {
        this.promiseState = "pending"
        this.promiseResult = null
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }
    
    resolve(value) {
        if(this.promiseState !== "pending") return

        this.promiseState = "fulfilled"
        this.promiseResult = value
    }
    
    reject(reason) {
        if(this.promiseState !== "pending") return

        this.promiseState = "rejected"
        this.promiseResult = reason
    }

    then(onFulfilled, onRejected) {
        if(this.promiseState === "fulfilled") {
            onFulfilled(this.promiseResult)
        }
        if(this.promiseState === "rejected") {
            onRejected(this.promiseResult)
        }
    }
}

七、验证 then 方法

image.png

八、先来看一下一个 promise 原生的例子

image.png image.png

  • 如上图所示,异步执行 resolve 方法时,p 在调用的那一刻是 pending 状态,5秒之后才会执行 resolve 方法。
  • 再看一下,我们的 MyPromise 是否支持这样的功能,如下图

image.png

  • 如上所示,我们的 MyPromise 目前不支持这样的功能。好的,没关系,我们来实现一下!

九、实现 resolve 和 reject 的异步触发

  • 思路:
    • 初始化的时候,创建成功的回调数组和失败的回调数组
    • 在 then 方法中,判断状态是否为 pending,如果是 pending,则将成功回调和失败回调分别添加到成功回调数组和失败回调数组
    • 在 resolve 方法和 reject 方法中,遍历回调函数数组,如果有则执行,并且执行后清除这一项
class MyPromise {
    constructor(executor) {
        this.initValue()
        this.initBind()

        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    initValue() {
        this.promiseState = "pending"
        this.promiseResult = null

        // 初始化的时候,创建成功的回调数组和失败的回调数组
        this.onFulfilledCallBacks = []
        this.onRejectedCallBacks = []
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }

    resolve(value) {
        if (this.promiseState !== "pending") return

        this.promiseState = "fulfilled"
        this.promiseResult = value

        // 遍历回调函数数组,如果有则执行,并且执行后清除这一项
        while (this.onFulfilledCallBacks.length) this.onFulfilledCallBacks.shift()(this.promiseResult)
    }

    reject(reason) {
        if (this.promiseState !== "pending") return

        this.promiseState = "rejected"
        this.promiseResult = reason

        // 遍历回调函数数组,如果有则执行,并且执行后清除这一项
        while (this.onRejectedCallBacks.length) this.onRejectedCallBacks.shift()(this.promiseResult)
    }

    then(onFulfilled, onRejected) {
        // 在 then 方法中,判断状态是否为 pending,如果是 pending,则将成功回调和失败回调分别添加到成功回调数组和失败回调数组
        if (this.promiseState === "pending") {
            this.onFulfilledCallBacks.push(onFulfilled)
            this.onRejectedCallBacks.push(onRejected)
        }
        if (this.promiseState === "fulfilled") {
            onFulfilled(this.promiseResult)
        }
        if (this.promiseState === "rejected") {
            onRejected(this.promiseResult)
        }
    }
}

十、验证 resolve 和 reject 的异步触发

image.png

十一、实现 then 的链式调用

class MyPromise {
    constructor(executor) {
        this.initValue()
        this.initBind()

        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    initValue() {
        this.promiseState = "pending"
        this.promiseResult = null

        // 初始化的时候,创建成功的回调数组和失败的回调数组
        this.onFulfilledCallBacks = []
        this.onRejectedCallBacks = []
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }

    resolve(value) {
        if (this.promiseState !== "pending") return

        this.promiseState = "fulfilled"
        this.promiseResult = value

        // 遍历回调函数数组,如果有则执行,并且执行后清除这一项
        while (this.onFulfilledCallBacks.length) this.onFulfilledCallBacks.shift()(this.promiseResult)
    }

    reject(reason) {
        if (this.promiseState !== "pending") return

        this.promiseState = "rejected"
        this.promiseResult = reason

        // 遍历回调函数数组,如果有则执行,并且执行后清除这一项
        while (this.onRejectedCallBacks.length) this.onRejectedCallBacks.shift()(this.promiseResult)
    }

    then(onFulfilled, onRejected) {
        var thenPromise = new MyPromise((resolve, reject) => {
            var handlePromiseCallback = cb => {
                queueMicrotask(() => {
                    try {
                        var x = cb(this.promiseResult)

                        if (x === thenPromise) {
                            throw "Can Not Return Self"
                            return
                        }

                        if (x instanceof MyPromise) {
                            x.then(resolve, reject)
                        } else {
                            resolve(x)
                        }
                    } catch (error) {
                        reject(error)
                    }

                })
            }

            // 在 then 方法中,判断状态是否为 pending,如果是 pending,则将成功回调和失败回调分别添加到成功回调数组和失败回调数组
            if (this.promiseState === "pending") {
                this.onFulfilledCallBacks.push(handlePromiseCallback.bind(this, onFulfilled))
                this.onRejectedCallBacks.push(handlePromiseCallback.bind(this, onRejected))
            }
            if (this.promiseState === "fulfilled") {
                // onFulfilled(this.promiseResult)
                handlePromiseCallback(onFulfilled)
            }
            if (this.promiseState === "rejected") {
                // onRejected(this.promiseResult)
                handlePromiseCallback(onRejected)
            }
        })

        return thenPromise
    }
}
  • 这一步,只改动了 then 方法。说一下,我们都做了什么:

    • then 方法返回一个 promise 对象,所以实现了链式调用
    • promise.then 是一个微任务,所以我们使用了 queueMicrotask API
    • 对回调函数进行递归调用,返回值不能等于自身。如果返回值是 promise 对象,则继续执行 then 方法;如果不是,则执行成功回调
  • 验证一下

image.png

十二、实现 then 方法的透传

  • 先来看一下什么是 then 方法的透传?

image.png

  • 所谓透传,就是在 then 方法中传入一个不是函数的值,在下一个 then 方法中依然可以获取到 resolve 或 reject 所传输的值
  • 在我们的 MyPromise 中实现一下
class MyPromise {
    constructor(executor) {
        this.initValue()
        this.initBind()

        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    initValue() {
        this.promiseState = "pending"
        this.promiseResult = null

        // 初始化的时候,创建成功的回调数组和失败的回调数组
        this.onFulfilledCallBacks = []
        this.onRejectedCallBacks = []
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }

    resolve(value) {
        if (this.promiseState !== "pending") return

        this.promiseState = "fulfilled"
        this.promiseResult = value

        // 遍历回调函数数组,如果有则执行,并且执行后清除这一项
        while (this.onFulfilledCallBacks.length) this.onFulfilledCallBacks.shift()(this.promiseResult)
    }

    reject(reason) {
        if (this.promiseState !== "pending") return

        this.promiseState = "rejected"
        this.promiseResult = reason

        // 遍历回调函数数组,如果有则执行,并且执行后清除这一项
        while (this.onRejectedCallBacks.length) this.onRejectedCallBacks.shift()(this.promiseResult)
    }

    then(onFulfilled, onRejected) {
        // 添加透传逻辑
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value
        onRejected = typeof onRejected === "function" ? onRejected : reason => reason

        var thenPromise = new MyPromise((resolve, reject) => {
            var handlePromiseCallback = cb => {
                queueMicrotask(() => {
                    try {
                        var x = cb(this.promiseResult)

                        if (x === thenPromise) {
                            throw "Can Not Return Self"
                            return
                        }

                        if (x instanceof MyPromise) {
                            x.then(resolve, reject)
                        } else {
                            resolve(x)
                        }
                    } catch (error) {
                        reject(error)
                    }

                })
            }

            // 在 then 方法中,判断状态是否为 pending,如果是 pending,则将成功回调和失败回调分别添加到成功回调数组和失败回调数组
            if (this.promiseState === "pending") {
                this.onFulfilledCallBacks.push(handlePromiseCallback.bind(this, onFulfilled))
                this.onRejectedCallBacks.push(handlePromiseCallback.bind(this, onRejected))
            }
            if (this.promiseState === "fulfilled") {
                // onFulfilled(this.promiseResult)
                handlePromiseCallback(onFulfilled)
            }
            if (this.promiseState === "rejected") {
                // onRejected(this.promiseResult)
                handlePromiseCallback(onRejected)
            }
        })

        return thenPromise
    }
}
  • 验证一下

image.png

十三、实现 catch、finally 方法

class MyPromise {
    constructor(executor) {
        this.initValue()
        this.initBind()

        try {
            executor(this.resolve, this.reject)
        } catch (error) {
            this.reject(error)
        }
    }

    initValue() {
        this.promiseState = "pending"
        this.promiseResult = null

        // 初始化的时候,创建成功的回调数组和失败的回调数组
        this.onFulfilledCallBacks = []
        this.onRejectedCallBacks = []
    }

    initBind() {
        this.resolve = this.resolve.bind(this)
        this.reject = this.reject.bind(this)
    }

    resolve(value) {
        if (this.promiseState !== "pending") return

        this.promiseState = "fulfilled"
        this.promiseResult = value

        // 遍历回调函数数组,如果有则执行,并且执行后清除这一项
        while (this.onFulfilledCallBacks.length) this.onFulfilledCallBacks.shift()(this.promiseResult)
    }

    reject(reason) {
        if (this.promiseState !== "pending") return

        this.promiseState = "rejected"
        this.promiseResult = reason

        // 遍历回调函数数组,如果有则执行,并且执行后清除这一项
        while (this.onRejectedCallBacks.length) this.onRejectedCallBacks.shift()(this.promiseResult)
    }

    then(onFulfilled, onRejected) {
        // 添加透传逻辑
        onFulfilled = typeof onFulfilled === "function" ? onFulfilled : value => value
        onRejected = typeof onRejected === "function" ? onRejected : reason => reason

        var thenPromise = new MyPromise((resolve, reject) => {
            var handlePromiseCallback = cb => {
                queueMicrotask(() => {
                    try {
                        var x = cb(this.promiseResult)

                        if (x === thenPromise) {
                            throw "Can Not Return Self"
                            return
                        }

                        if (x instanceof MyPromise) {
                            x.then(resolve, reject)
                        } else {
                            resolve(x)
                        }
                    } catch (error) {
                        reject(error)
                    }

                })
            }

            // 在 then 方法中,判断状态是否为 pending,如果是 pending,则将成功回调和失败回调分别添加到成功回调数组和失败回调数组
            if (this.promiseState === "pending") {
                this.onFulfilledCallBacks.push(handlePromiseCallback.bind(this, onFulfilled))
                this.onRejectedCallBacks.push(handlePromiseCallback.bind(this, onRejected))
            }
            if (this.promiseState === "fulfilled") {
                // onFulfilled(this.promiseResult)
                handlePromiseCallback(onFulfilled)
            }
            if (this.promiseState === "rejected") {
                // onRejected(this.promiseResult)
                handlePromiseCallback(onRejected)
            }
        })

        return thenPromise
    }

    catch(onRejected) {
        return this.then(null, onRejected)
    }

    finally(cb) {
        return this.then(
            value => MyPromise.resolve(cb()).then(() => value),
            reason => MyPromise.resolve(cb()).then(() => reason)
        )
    }
}

十四、实现静态方法 resolve、reject

static resolve(value) {
    if (value instanceof MyPromise) return value

    return new MyPromise((resolve, reject) => {
        resolve(value)
    })
}

static reject(reason) {
    return new MyPromise((resolve, reject) => {
        reject(reason)
    })
}

十五、实现静态方法 all、race、allsettled、any

static all(promises) {
    promises = Array.isArray(promises) ? promises : []

    var index = 0
    var result = []

    return new MyPromise((resolve, reject) => {
        if (promises.length === 0) {
            resolve(result)
            return
        }

        for (var i = 0; i < promises.length; i++) {
            MyPromise.resolve(promises[i]).then(value => {
                result[i] = value
                if (++index === promises.length) {
                    resolve(result)
                }
            }).catch(reason => {
                reject(reason)
            })
        }
    })
}

static race(promises) {
    promises = Array.isArray(promises) ? promises : []

    return new MyPromise((resolve, reject) => {
        for (let promise of promises) {
            MyPromise.resolve(promise).then(value => {
                resolve(value)
            }).catch(reason => {
                reject(reason)
            })
        }
    })
}

static allsettled(promises) {
    promises = Array.isArray(promises) ? promises : []

    var index = 0
    var result = []

    return new MyPromise((resolve, reject) => {
        if (promises.length === 0) {
            resolve(result)
            return
        }

        function handleResult(i, value, state) {
            result[i] = {
                value,
                state
            }
            if (++index === promises.length) {
                resolve(result)
            }
        }

        for (var i = 0; i < promises.length; i++) {
            MyPromise.resolve(promises[i]).then(value => {
                handleResult(i, value, "fulfilled")
            }).catch(reason => {
                handleResult(i, reason, "rejected")
            })
        }
    })
}

static any(promises) {
    promises = Array.isArray(promises) ? promises : []

    var index = 0

    return new MyPromise((resolve, reject) => {
        for (let promise of promises) {
            MyPromise.resolve(promise).then(value => {
                resolve(value)
            }).catch(reason => {
                if (++index === promises.length) {
                    reject("All Promises are Rejected")
                }
            })
        }
    })
}