🎢前端进阶:手写Promise实现(二)错误收集 & onRejected注入🚀

133 阅读4分钟

✨ 摘要

在上一篇中,我们实现了简易版 Promise 的链式调用功能。但是不能只有 resolve 没有 reject,这就像只会夸人不会批评——不完整!😅

这篇文章就来继续完善,实现完整的 reject - catch - finally 流程支持。


📜 官方Promise demo Mvp

const violet = new Promise((res, rej) => {
    console.log('1. real promise log')
    rej('2. error info is reject')
}).then(() => {}, (err) => {
    console.log(err, '---get error')
    return new Promise((res, rej) => {
        res('3. hello world!!!')
    })
}).then((res) => {
    console.log(res, '\n4. second then is running')
})

🔍 控制台打印结果

1. real promise log
2. error info is reject ---get error
3. hello world!!! 
4. second then is running

解释一下:

  • 第一个 Promise 中执行 rej,所以走 then 的第二个参数 onRejected
  • onRejected 返回一个新的 Promise,进入下一个 then
  • 整体流程仍然是链式的,reject 不会中断链条(只要你正确处理)

🚫 Reject 流程实现

我们要补充一个 reject 方法,它的结构和 resolve 类似,不过重点在于:

  • 如果 reject 的参数本身是一个 thenable,我们也需要链式等待它完成
  • 在 handle 调用时,正确触发 onRejected
  • 最后也需要遵守Promise A+规范,使用setTimeout进行exec
function reject (error) {
    const fn = () => {
        if (state !== STATUS_ENUM.PENDING) return
        if (error && (typeof error === 'object' || typeof error === 'function')) {
            const { then: customThenFn } = error
            if (typeof customThenFn === 'function') {
                customThenFn.call(error, resolve, reject)
                return
            }
        }
        state = STATUS_ENUM.REJECTED
        value = error
        handleCb()
    }

    setTimeout(fn, 0) // 异步触发,符合 Promise A+ 规范
}

🧠 回到链式调用逻辑

来看这段代码中的核心逻辑:

const violet = new Promise((res, rej) => {
    console.log('1. real promise log')
    rej('2. error info is reject')
}).then(() => {}, (err) => {
    console.log(err, '---get error')
    return new Promise((res, rej) => {
        res('3. hello world!!!')
    })
})

你可能已经注意到——

❗ 那个handle函数还不够(因为没有调用onRejected方法)

初始版本的 handle 只支持处理 resolve:

function handle (callback) {
    if (state === STATUS_ENUM.PENDING) {
        callbacks.push(callback)
        return
    }
    if (state === STATUS_ENUM.FULFILLED) {
        if (!callback.onFulfilled) {
            callback.resolve(value)
            return
        }
        const ret = callback.onFulfilled(value)
        callback.resolve(ret)
    }
}

但是我们现在有 reject !我们必须扩展它:

function handle (callback) {
    if (state === STATUS_ENUM.PENDING) {
        callbacks.push(callback)
        return
    }

    const isFulfilled = state === STATUS_ENUM.FULFILLED
    const cb = isFulfilled ? callback.onFulfilled : callback.onRejected
    const next = isFulfilled ? callback.resolve : callback.reject

    if (!cb) {
        next(value)
        return
    }

    try {
        const ret = cb(value)
        next(ret)
    } catch (err) {
        callback.reject(err)
    }
}

改进点

  • try...catch 捕获执行错误,避免未捕获异常
  • 状态判断明确,支持 rejected 状态回调

🧩 then 方法注入 onRejected

我们还要确保 .then(onFulfilled, onRejected) 能把 onRejected 正确传入:

this.then = function (onFulfilled, onRejected) {
    return new ViPromise ((resolve, reject) => {
        handle({
            onFulfilled,
            onRejected,
            resolve,
            reject
        })
    })
}

最后,别忘了运行用户的 executor 函数(也要传入reject):

fn(resolve, reject) // 执行用户传入的函数

✅ ViPromise reject 测试

我们重新跑一遍验证逻辑:

const violet = new ViPromise((res, rej) => {
    console.log('1. real promise log')
    rej('2. error info is reject')
}).then(() => {}, (err) => {
    console.log(err, '---get error')
    return new Promise((res, rej) => {
        res('3. hello world!!!')
    })
}).then((res) => {
    console.log(res, '\n4. second then is running')
})

控制台打印:

1. real promise log
2. error info is reject ---get error
3. hello world!!! 
4. second then is running

流程和原生 Promise 一致,说明我们的 ViPromise 的 reject 支持已经 ✅OK!


🚫 catch 异常处理实现

对于程序产生的错误,我们通常使用try...catch进行捕获,并设置promise状态为reject即可,于是我们补充handle逻辑

function handle (callback) {
    if (state === STATUS_ENUM.PENDING) {
        callbacks.push(callback)
        return
    }
    const isFulfilled = state === STATUS_ENUM.FULFILLED
    const cb = isFulfilled ? callback.onFulfilled : callback.onRejected
    const next = isFulfilled ? callback.resolve : callback.reject
    // 没传入,默认reject
    if (!cb) {
        next(value)
        return
    }

    try {
        const ret = cb(value)
        next(ret)
    } catch (e) {
        callback.reject(e)
    }
}

然后还需要创建一个catch方法,只是声明式我们定义catch用来接受错误的处理,但是其实也是直接调用then方法

this.catch = function (onError) {
    this.then(null, onError)
}

✅ ViPromise catch 测试

我们修改样例:

const violet = new ViPromise((res, rej) => {
    console.log('1. real promise log')
    rej('2. error info is reject')
}).then(() => {}, (err) => {
    console.log(err, '---get error')
    return new Promise((res, rej) => {
        // res('3. hello world!!!')
        rej('报错了!混蛋!!!')
    })
}).then((res) => {
    console.log(res, '\n4. second then is running')
}).catch(e => {
    console.log(e, '---catch is running')
})

控制台打印:

1. real promise log
2. error info is reject ---get error
报错了!混蛋!!! ---catch is running

🚀 Finally 处理实现

其实在程序最后,不管Promise状态如何,会有业务需要进行一些收尾工作,这常常就会放在finally中进行,所以finally的执行并不关心promise的成功与否

function Promise(fn){ 
    ...
    this.catch = function (onError){
        this.then(null,onError)
    }
    this.finally = function (onDone){
        this.then(onDone,onDone)
    }
    ...
}

🧾 总结

🔧 到目前为止,我们完成了 ViPromise 的核心功能:

  • ✅ 支持 resolve
  • ✅ 支持 reject
  • ✅ 支持 catch语法糖
  • ✅ 支持 finally
  • ✅ 支持 then 链式调用,错误回调
  • ✅ 异步执行,符合 A+ 规范

👀 你是否也实现过自己的 Promise?欢迎在评论区贴出你的实现,或者告诉我你踩过哪些坑!