Promise.prototype.catch以及Promise.prototype.finally原理解析以及实现

260 阅读4分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第11天,点击查看活动详情

前言

本篇将讲解Promise.prototype.catch以及Promise.prototype.finally的细则以及实现,希望通过阅读这篇文章能让你完全掌握。

阅读这篇文章,需要你掌握的知识👇

  • Promise基本知识

其中Promise基础知识如果不了解或有遗忘的同学可以参考我这篇文章:《简单明了的Promise基本知识点》

本文需要使用的实现类代码👇

class myPromise{
    static PENDING = 'pending';
    static FULFILLED = 'fulfilled';
    static REJECTED = 'rejected';
    constructor(func){
        this.PromiseState =  myPromise.PENDING
        this.PromiseResult = undefined
        this.onFulfilledCallbacks = []
        this.onRejectedCallbacks = []
        try{
            func(this.resolve.bind(this),this.reject.bind(this))
        }catch (e){
            this.reject(e)
        }
    }
    resolve(result){
        if(this.PromiseState == myPromise.PENDING){
            this.PromiseState = myPromise.FULFILLED
            this.PromiseResult = result
            setTimeout(()=>{
                this.onFulfilledCallbacks.forEach(callback=>{callback(result)})
            })
​
        }
    }
    reject(reason){
        if (this.PromiseState == myPromise.PENDING){
            this.PromiseState = myPromise.REJECTED
            this.PromiseResult = reason
            setTimeout(()=>{
                this.onRejectedCallbacks.forEach(callback=>{callback(reason)})
            })
​
        }
    }
    then(onFulfilled,onRejected){
        onFulfilled = typeof onFulfilled == 'function' ? onFulfilled : value => value
        onRejected = typeof onRejected == 'function' ? onRejected : reason =>{ throw reason }
        const promise2 = new myPromise((resolve,reject)=>{
            if (this.PromiseState == myPromise.FULFILLED){
                setTimeout(()=>{
                    try {
                        const value = onFulfilled(this.PromiseResult)
                        if (value != undefined){
                            resolvePromise(promise2,value,resolve,reject)
                        }
                    }catch (e){
                        reject(e)
                    }
                })
            }else if(this.PromiseState == myPromise.REJECTED){
                setTimeout(()=>{
                    try {
                        const value = onRejected(this.PromiseResult)
                        if (value != undefined){
                            resolvePromise(promise2,value,resolve,reject)
                        }
                    }catch (e){
                        reject(e)
                    }
                })
            }else if(this.PromiseState === myPromise.PENDING){
                this.onFulfilledCallbacks.push(onFulfilled)
                this.onRejectedCallbacks.push(onRejected)
            }
        })
        return promise2
    }
}
function resolvePromise(promise2,x,resolve,reject){
    if (promise2  == x){
        return reject(new TypeError())
    }
    //2.3.2判断传入参数是否为Promise
    if (x instanceof myPromise){
        //2.3.2.1 如果 `x` 处于等待状态, `promise` 需保持为等待态直至 `x` 被执行或拒绝
        if (x.PromiseState == myPromise.PENDING){
            resolvePromise(promise2,x,resolve,reject)
        }else if(x.PromiseState == myPromise.FULFILLED){
            // 2.3.2.2 如果 x 处于执行态,用相同的值执行 promise
              resolve(x.PromiseResult);
        }else if (x.PromiseState === myPromise.REJECTED) {
            // 2.3.2.3 如果 x 处于拒绝态,用相同的据因拒绝 promise
              reject(x.PromiseResult);
        }
    }else if(x != null && (typeof x === 'object' || typeof x == 'function')){
        let then;
        try {
            then = x.then
        }catch (e) {
            reject(e)
        }
        if (typeof then == 'function'){
            //2.3.3.3.3 如果 `resolvePromise` 和 `rejectPromise` 均被调用,或者被同一参数调用了多次,则优先采用首次调用并忽略剩下的调用
            let called = false
            try{
                then.call(x,
                    // 2.3.3.3.1 如果 resolvePromise 以值 y 为参数被调用,则运行 [[Resolve]](promise, y)
                    y =>{
                        if (called)return //如果已调用,则采取首次调用
                        called = true
                        resolvePromise(promise2, y, resolve, reject);
                    },
                    // 2.3.3.3.2 如果 rejectPromise 以据因 r 为参数被调用,则以据因 r 拒绝 promise
                    r =>{
                        if (called)return //如果已调用,则采取首次调用
                        called = true
                        reject(r);
                    })
            }catch (e) {
                //2.3.3.3.4 如果调用 `then` 方法抛出了异常 `e`:
                //2.3.3.3.4.1 如果 `resolvePromise` 或 `rejectPromise` 已经被调用,则忽略之
                if (called)return
                //2.3.3.3.4.2 否则以 `e` 为据因拒绝 `promise`
                called = true
                reject(e)
            }
        }else{
            //2.3.3.4 如果 `then` 不是函数,以 `x` 为参数执行 `promise`
            resolve(x)
        }
    }
}
​

什么是Promise.prototype.catch

Promise.prototype.catch作为Promise类实例属性具有的方法,

  • 接收一个回调函数参数
  • Promise.prototype.catch 返回一个Promise,并且处理拒绝的情况。
  • 它的行为与调用Promise.prototype.then(undefined, onRejected) 相同。

实现 Promise.prototype.catch

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

如上代码为什么这么简单?

由于官方规定catch(onRejected)与调用Promise.prototype.then(undefined, onRejected) 相同。

我们只好这么写咯🤡

测试代码

const myPromise = require('./promiseOtherAPI')
​
var p1 = new myPromise(function (resolve, reject) {
    resolve('Success');
});
​
p1.then(function (value) {
    console.log(value); // "Success!"
    throw 'oh, no!';
}).catch(function (e) {
    console.log(e); // "oh, no!"
}).then(function () {
    console.log('after a catch the chain is restored');
}, function () {
    console.log('Not fired due to the catch');
});
​
// 以下行为与上述相同
p1.then(function (value) {
    console.log(value); // "Success!"
    return Promise.reject('oh, no!');
}).catch(function (e) {
    console.log(e); // "oh, no!"
}).then(function () {
    console.log('after a catch the chain is restored');
}, function () {
    console.log('Not fired due to the catch');
});
​
// 捕获异常
const p2 = new myPromise(function (resolve, reject) {
    throw new Error('test');
});
p2.catch(function (error) {
    console.log(error);
});
// Error: test

控制台结果👇

Success
Success
Error: test
oh, no!
oh, no!
after a catch the chain is restored
after a catch the chain is restored

如果你的输出结果和以上一致,就代表测试通过了🤩

什么是Promise.prototype.finally

Promise.prototype.finally作为Promise类实例属性具有的方法,

  • 接收一个回调函数作为参数
  • 返回一个Promise
  • 在promise结束时,无论结果是fulfilled或者是rejected,都会执行指定的回调函数。

实现 Promise.prototype.finally

class myPromise {
    ...
    /**
     * finally
     * @param {*} callBack 无论结果是fulfilled或者是rejected,都会执行的回调函数
     * @returns 
     */
   finally(callBack) {
      return this.then(callBack, callBack)
  }
}

如上代码中,由于finally方法不用参考执行时Promise的状态,但又会返回一个新的Promise并且执行回调函数。于是我们可以巧妙的使用then方法,从而达到了

  • 异步执行
  • 参数校验
  • 返回一个符合规范的Promise

测试代码

const myPromise = require('./promiseOtherAPI')
​
let p1 = new Promise(function (resolve, reject) {
    resolve(1)
}).then(function (value) {
    console.log(value);
}).catch(function (e) {
    console.log(e);
}).finally(function () {
    console.log('finally');
});

输出结果:

1
finally

如果你的输出结果和以上一致,就代表测试通过了🤩

尾声

这俩个api的实现较为简单,我就合着一篇文章一起讲解了。越写越发现实现Promise系列的难点是去记这么多规则了,希望同学们牢记规则。

\