手写一个自己的Promise(三)

系列文章目录

根据PromiseA+规范,自己手动实现一个Promise



前言

在上一篇文章中,我们已经实现了MyPromise关于then方法的功能实现,本篇文章实现catch、finally、resolve、reject和all这五个方法,这些方法在Promise A+规范中并没有提及,而是由ES6提出并实现的,因此本篇文章参考了MDN的相关文档,具体链接放在了最后。


一、catch方法

MDN关于catch方法的描述是它和then方法一样,只是专门用于处理失败的回调,因此最简单的方法就是在catch方法中再返回then方法,把接收的回调作为then方法的onRejected回调函数传入即可

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

执行试一试
请添加图片描述
运行结果没有问题

二、finally方法

MDN关于finally方法的描述是:finally方法类似于调用 then(onFinally, onFinally),无论Promise执行的是成功还是失败,都需要执行onFinally方法,同时onFinally 回调函数不接收任何参数,finally() 调用通常是透明的,不会更改原始 promise 的状态。因此实现思路就是在finally函数中还是返回then方法,但无论成功还是失败都调用onFinally,之后如果是成功则返回成功的结果,失败则抛出失败的原因,实现和then方法中一样的状态穿透效果。

finally(onFinally) {
    return this.then((data) => {
        onFinally()
        return data
    }, (error) => {
        onFinally()
        throw error
    })
}

执行试一试
请添加图片描述
运行结果没有问题

三、resolve方法

MDN关于resolve方法的描述:resolve方法是一个静态方法,接收一个参数value,用于直接将给定的值转换为一个 Promise。而根据参数value传入的不同又分为几种情况

  • 如果value是一个Promise对象,则直接返回
  • 如果value是一个thenable(这个thenable就是上一篇文章中提到过的满足Promise A+规范的Promise:即实现了then方法东西,这里可以借助上一篇文章中就已经实现的isPromiseLike函数进行判断),调用这个thenable的then方法,将Promise实例的resolve和reject作为参数传入
  • 如果value不是以上任何情况,调用resolve方法,把value作为resolve的参数

最后返回Promise的实例

static resolve(value) {
    if (value instanceof MyPromise) return value // 如果是一个MyPromise实例
    let _resolve, _reject;
    const p = new MyPromise((resolve, reject) => {  // 创建一个实例
        _resolve = resolve
        _reject = reject
    })
    if (p.#isPromiseLike(value)) {  // 如果是一个thenable
        value.then(_resolve, _reject)  // 调用value的then方法,把保存下来的resolve和reject方法作为参数传入
    } else {
        _resolve(value)   // 说明什么都不是,那直接返回一个MyPromise实例
    }
    return p
}

测试一下,首先是value是MyPromise的情况
请添加图片描述
通过MyPromise返回的就是传入的value,没有问题
然后是传入thenable的情况

请添加图片描述
结果也没用问题,最后则是随便传入其他数据
请添加图片描述
运行结果没有问题

这里有一个注意点,当第二种情况时,因为我们要实现的resolve是一个静态方法,而MyPromise实例的resolve、reject和比较函数isPromiseLike都是实例方法,静态方法不能直接调用实例方法,因此这里的解决方案就是先提前创建两个变量_resolve和_reject,再创建一个MyPromise的实例,用直接创建的变量保存实例中的resolve与reject方法;而isPromiseLike调用时则是通过实例调用。

四、reject方法

MDN关于reject方法的描述是:Promise.reject 静态方法返回一个被拒绝的 Promise 对象。通过使用 Error 的实例获取错误原因 reason 。因此实现起来很简单,直接返回一个Promise实例,调用实例的reject方法,把传递value作为的错误原因传入即可。

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

请添加图片描述

五、all方法

MDN关于all方法描述:Promise.all() 静态方法接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现(即使传入的是一个空的可迭代对象),并返回一个包含所有兑现值的数组。如果输入的任何 Promise 被拒绝,则返回的 Promise 将被拒绝,并带有第一个被拒绝的原因。
all方法会接收一个可迭代的对象,因此这里只能用for…of进行循环判断当前传入的具体数量,定为count,如果count创建一个MyPromise实例,与resolve方法类似,将resolve与reject方法提前存起来,然后判断可迭代对象是否是0,如果是0,直接调用resove,把最后的结果传入,否则变量这个可迭代对象,先将每一项都通过MyPromise.resolve变成一个MyPromise实例,再调用then方法,因为如果一个MyPromise实例失败则整体失败,因此then方法中的onReject方法位置直接传入之前保存的_reject即可,而onFulFilled则需要将当前回调的传入结果作为参数加入到结果数组result的指定位置,最后判断当完成的MyPromise数量等于传入的可迭代数量,调用保存的_resolve方法并把结果数组作为参数传入。最后返回MyPromise实例。

static all(proms) {
        let _resolve, _reject;
        const p = new MyPromise((resolve, reject) => {
            _resolve = resolve
            _reject = reject
        })
        let count = 0;  // 传入的可迭代的数量
        let result = []; // 最后的结果
        let fulfilledCount = 0; // 完成的MyPromise的数量
        for (const prom of proms) {
            const i = count
            count++
            MyPromise.resolve(prom).then((data) => {
                // 将成功的数据汇总到 result
                result[i] = data
                fulfilledCount++
                // 判断是不是全部完成
                if (fulfilledCount === count) {
                    return _resolve(result)
                }
            },_reject)
        }
        if (count===0) {
            _resolve(result)
        }
        return p
    }

测试一下
请添加图片描述
请添加图片描述
结果没有问题。
至此,catch、finally、resolve、reject和all方法全部完成

总结

本篇文章,完成了ES6中关于Promise的catch方法、finally方法、resolve方法、reject方法和all方法的实现。最后,附上MyPromise的完整代码

const PENDING = 'pending'
const FULFILLED = 'fulfilled'
const REJECTED = 'rejected'

class MyPromise {

    #state = PENDING
    #result = undefined

    #handler = []

    // 判断是否是符合Promise A+规范的promise
    #isPromiseLike(value) {
        if (value && (typeof value === 'object' || typeof value === 'function')) {
            return typeof value.then === 'function'
        }
        return false
    }

    // 将任务放入异步队列里
    #runMicroTask(func) {
        if (typeof process === 'object' && typeof process.nextTick === 'function') {  // 模拟node环境微队列执行
            process.nextTick(func)
        } else if (typeof MutationObserver === 'function') {  // 模拟浏览器环境的微队列执行
            const ob = new MutationObserver(func)  // MutationObserver是一个观察器,它会自动将一个回调函数放入微队列
            const textNode = document.createTextNode('1')  // 构建一个文本节点用于观察器观察
            ob.observe(textNode, {  // 指定观察器观察的节点
                characterData: true // 观察字符的变化
            })
            textNode.data = '2'  // 手动修改文本节点,当被观察的对象发生变化时,观察器会把回调放入微队列执行,从而实现模拟微队列
        } else {
            setTimeout(func, 0)  // 既不是node环境也不是浏览器,只能用setTimeout来实现异步了
        }
    }

    #runOne(callback, resolve, reject) {  // callback 回调函数(onFulfilled||onRejected)
        this.#runMicroTask(() => {  // 将之前的逻辑通过一个箭头函数的形式放入runMicroTask实现模拟放入微队列
            if (typeof callback !== 'function') {  // 判断传入的回调函数是否是函数,不是则走穿透逻辑
                const settled = this.#state === FULFILLED ? resolve : reject // 定义一个变量settled,根据当前的state判断此次应该执行什么函数
                settled(this.#result)
                return;  // 确定不是函数,后面的逻辑也不会再走了
            }
            try {
                const data = callback(this.#result)
                if (this.#isPromiseLike(data)) { // 回调函数正常执行且回调函数的返回结果是个Promise,需要调用返回结果的then方法,并把resolve和reject传进去,由返回结果的Promise判断应该执行哪个
                    data.then(resolve, reject)
                } else {
                    resolve(data)  // 回调函数正常执行且回调函数的返回结果不是个Promise,执行resolve
                }
            } catch (error) {
                reject(error)   // 捕获到了回调函数执行时抛出的错误,此时无论state是什么都会执行reject
            }
        })
    }

    #run() {
        if (this.#state === PENDING) return
        while (this.#handler.length) {
            const { onFulfilled, onRejected, resolve, reject } = this.#handler.shift()
            if (this.#state === FULFILLED) {
                this.#runOne(onFulfilled, resolve, reject) // 成功传递onFulfilled
            } else if (this.#state === REJECTED) {
                this.#runOne(onRejected, resolve, reject)// 失败传递onRejected
            }
        }
    }

    constructor(executor) {
        const resolve = (data) => {
            this.#changeState(FULFILLED, data)
        }
        const reject = (reason) => {
            this.#changeState(REJECTED, reason)
        }
        try {
            executor(resolve, reject)
        } catch (error) {
            reject(error)
        }
    }
    #changeState(state, result) {
        if (this.#state !== PENDING) return
        this.#state = state
        this.#result = result
        this.#run()
    }

    then(onFulfilled, onRejected) {
        return new MyPromise((resolve, reject) => {
            this.#handler.push({
                onFulfilled, onRejected, resolve, reject
            })
            this.#run()
        })
    }

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

    finally(onFinally) {
        return this.then((data) => {
            onFinally()
            return data
        }, (error) => {
            onFinally()
            throw error
        })
    }

    static resolve(value) {
        if (value instanceof MyPromise) return value // 如果是一个MyPromise实例
        let _resolve, _reject;
        const p = new MyPromise((resolve, reject) => {  // 创建一个实例
            _resolve = resolve
            _reject = reject
        })
        if (p.#isPromiseLike(value)) {  // 如果是一个thenable
            value.then(_resolve, _reject)  // 调用value的then方法,把保存下来的resolve和reject方法作为参数传入
        } else {
            _resolve(value)   // 说明什么都不是,那直接返回一个MyPromise实例
        }
        return p 
    }

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

    static all(proms) {
        let _resolve, _reject;
        const p = new MyPromise((resolve, reject) => {
            _resolve = resolve
            _reject = reject
        })
        let count = 0;  // 传入的Promise的数量
        let result = []; // 最后的结果
        let fulfilledCount = 0; // 完成的Promise的数量
        for (const prom of proms) {
            const i = count
            count++
            MyPromise.resolve(prom).then((data) => {
                // 将成功的数据汇总到 result
                result[i] = data
                fulfilledCount++
                // 判断是不是全部完成
                if (fulfilledCount === count) {
                    return _resolve(result)
                }
            },_reject)
        }
        if (count===0) {
            _resolve(result)
        }
        return p
    }
}

MDN文档描述
MDN关于catch方法的文档描述
MDN关于finally方法的文档描述
MDN关于resolve方法的文档描述
MDN关于reject方法的文档描述
MDN关于all方法的文档描述

  • 32
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
很高兴为您提供一个手写Promise 实现,代码如下: class Promise { constructor(executor) { this.state = 'pending'; this.value = undefined; this.reason = undefined; this.onResolvedCallbacks = []; this.onRejectedCallbacks = []; const resolve = (value) => { if (this.state === 'pending') { this.state = 'fulfilled'; this.value = value; this.onResolvedCallbacks.forEach((fn) => fn()); } }; const reject = (reason) => { if (this.state === 'pending') { this.state = 'rejected'; this.reason = reason; this.onRejectedCallbacks.forEach((fn) => fn()); } }; try { executor(resolve, reject); } catch (error) { reject(error); } } then(onFulfilled, onRejected) { onFulfilled = typeof onFulfilled === 'function' ? onFulfilled : (value) => value; onRejected = typeof onRejected === 'function' ? onRejected : (reason) => { throw reason }; const promise2 = new Promise((resolve, reject) => { if (this.state === 'fulfilled') { setTimeout(() => { try { const x = onFulfilled(this.value); Promise.resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }, 0); } if (this.state === 'rejected') { setTimeout(() => { try { const x = onRejected(this.reason); Promise.resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }, 0); } if (this.state === 'pending') { this.onResolvedCallbacks.push(() => { setTimeout(() => { try { const x = onFulfilled(this.value); Promise.resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }, 0); }); this.onRejectedCallbacks.push(() => { setTimeout(() => { try { const x = onRejected(this.reason); Promise.resolvePromise(promise2, x, resolve, reject); } catch (error) { reject(error); } }, 0); }); } }); return promise2; } static resolvePromise(promise2, x, resolve, reject) { if (promise2 === x) { return reject(new TypeError('Chaining cycle detected for promise')); } let called = false; if (x instanceof Promise) { x.then((value) => { Promise.resolvePromise(promise2, value, resolve, reject); }, reject); } else if (x !== null && (typeof x === 'object' || typeof x === 'function')) { try { const then = x.then; if (typeof then === 'function') { then.call(x, (value) => { if (called) return; called = true; Promise.resolvePromise(promise2, value, resolve, reject); }, (reason) => { if (called) return; called = true; reject(reason); }); } else { resolve(x); } } catch (error) { if (called) return; called = true; reject(error); } } else { resolve(x); } } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值