Promise 系列(完)-- all、Promise.resolve、finally、catch 方法的实现

179 阅读6分钟

写在开头:如果被大佬们看到了有错误还请斧正,如果把萌新带到了沟里,你在爬出来就好了(手动滑稽)

myPromise 中的 then 方法已经完善的差不多了,目前已经能够支持大部分场景的使用,但是 Promise 除了 then 方法之外还有其他的方法的

all 方法的实现

  1. all 方法是用来解决异步并发问题的
  2. all 方法允许程序按照异步代码调用的顺序执行异步代码。
  3. 首先 all 方法会接收一个数组作为参数,在数组中可以填入任何值,这个数组中填入的顺序就是执行之后得到的顺序,all 方法的返回值也是一个 Promise 对象,所以可以在 all 方法后面链式调用 then 方法。
  4. all 方法中的每一项如果都是成功的那么结果就是成功的,如果有一个是失败的,那么结果就是失败的。
//  all 方法是利用一个 Promise 类 . 了一个方法,那么 all 方法应该是一个静态方法
Promise.all(['a', 1, fun()]);

既然 all 方法是一个静态方法,那么就需要在 myPromise 这个类中定义一个静态的 all 方法

class myPromise {
*
*
//  all 方法会接收一个数组作为参数
static all(array) {
    //  创建一个结果数组用来盛放 all 方法传进来的数组
    let result = [];
    //  声明一个方法将循环的每一项放入到 result 数组中
    function add(index, val) {
        result[index] = val;
    }
        //  由于 all 方法返回的也是一个 Promise 对象,所以在这里创建一个 Promise对象
    return new myPromise((resolve, reject) => {
        //  接收的既然是一个数组那就循环这个数组
        //  看这个数组传递进来的每一项是一个普通值还是一个 Promise 对象
        //  如果是普通值就直接将这个值放入结果数组中
        //  如果是 Promise 对象,就先执行这个 Promise 对象,在将这执行的结果放入结果数组中
        for (let i = 0; i< array.length; i++) {
            let current = array[i];
            if (current instanceof myPromise) {
                current.then((value) => {
                    add(i, value);
                }, (reason) => {
                    reject(reason);
                })
            } else {
                add(i, array[i]);
            }
        }
        resolve(result);
    })
}
*
*
}

这样 all 方法就被简单的实现了,运行,康康结果如何

截屏2021-04-08 下午8.23.09.png 奈斯~ 就很棒,能运行,不过 <1 empty item> 是个什么玩意儿?这里出现的不应该是 fn1 么?(又一次白给)回头看代码看看怎么肥四吧:

截屏2021-04-08 下午8.30.51.png

  1. 在 for 循环中,for 循环是直接执行完成的
  2. 但是由于有异步代码的延迟执行在 for 循环的执行过程中并没有等待 fn1 的结果
  3. 在 for 循环结束的时候又直接调用了 resolve 结束了这个状态
  4. 此时 fn1 还没有执行完
  5. 所以在输出的时候这里就被输出了空值

啊~ 原来是仗都打完了,貂蝉还在骑马来的路上;现在需要等等骑马来的路上的貂蝉

static all(array) {
    let result = [];
    //  添加一个标志位
    let index = 0;
    return new myPromise((resolve, reject) => {
        function add(key, val) {
            result[key] = val;
            //  每一次调用 add 都使得 index++ 
            index++;
            //  如果 index 的长度和数组的长度相等,那么也就意味着所有的调用都已经完成,这个时候在 resolve 结果数组
            if(index === array.length) {
                resolve(result);
            }
        }
        for (let i = 0; i< array.length; i++) {
            let current = array[i];
            if (current instanceof myPromise) {
                current.then((value) => {
                    add(i, value)
                }, (reason) => {
                    reject(reason)
                })
            } else {
                add(i, array[i])
            }
        }
        resolve(result);
    })
}

再次运行之前有问题的代码:

截屏2021-04-08 下午9.03.28.png 没问题!貂蝉赶上了。all 方法的实现也没有问题了。

Promise.resolve 方法的实现

Promise.resolve 方法的作用是将给定的值转换成为 Promise 对象,也就是说这个方法的返回值就是一个 Promise 对象。

截屏2021-04-09 上午11.14.02.png

Promise.resolve 方法也能接收一个 Promise 对象,在 resolve 方法中会进行判断,如果接收的是 Promise 对象那么就直接返回这个 Promise 对象。

截屏2021-04-09 上午11.24.37.png

和 all 方法一样 Promise.resove 方法是一个静态方法,所以需要用 static 关键词进行创建

static resolve(value) {
    //  查看接收的值是不是一个 Promise 对象,是就直接返回,不是就把他创建成 Promise 对象并返回
    if (value instanceof myPromise) {
        return value;
    }
    return new myPromise((resolve, reject) => {
        resolve(value);
    })
}

运行起来康康有没有问题

截屏2021-04-09 上午11.41.55.png 得到了想要的输出结果,没有问题,那么 Promise.resolve 方法就被实现了

finally 方法的实现

finally 方法特点:

  1. finally 方法中可以读取到 Promise 对象的执行结果。
  2. 无论当前的 Promise 对象状态是成功的还是失败的,finally 方法中传入的回调函数都是会得到执行的
  3. finally 方法回调函数中可以继续返回 Promise 对象
function fn() {
    return new Promise((resolve, reject) => {
        resolve('fn');
    })
}
fn().finally(() => {
    console.log('finally 方法')
}).then((value) => {
    console.log(value)
})

从使用上分析:finally 方法和 then 方法一样是 Promise 原型对象上的一个方法,所以定义方法和 then 方法相同

// finally 方法接收一个回调函数作为参数,因为无论当前的 Promise 对象执行的结果是成功或失败都要调用这个回调函数
finally(callback) {
    // 使用 then 方法可以在 finally 方法的内部得到当前 Promise 的状态
    // finall 方法后面还可以链式调用 then 方法,所以 finally 方法还需要返回一个 Promise 对象
    return this.then((value) => {
        callback();
        return value;
    }, (reason) => {
        callback();
        throw reason;
    })
}

接着运行起来康康结果

截屏2021-04-09 下午12.11.42.png

没问题,接下来再加上异步的处理逻辑完善 finally 方法

finally(callback) {
    return this.then((value) => {
        // 借助 myPromise.resolve 方法无论 callback 返回值是不是 Promise 都转为 Promise 在执行
        return myPromise.resolve(callback()).then(() => {
            value
        });
    }, (reason) => {
        return myPromise.resolve(callback()).then(() => {
            throw reason;
        });
    });
}

catch 方法

  1. catch 方法是用来处理当前这个 Promise 为失败状态下的情况的
  2. 在调用 then 方法的时候是可以不用传递失败回调的,直接被 catch 方法捕捉
// 既然只是捕捉失败,那么在 catch 方法捕捉错误信息的时候只传递失败回调就可以了
catch(failCallback) {
    return this.then(undefind, failCallback);
}

运行起来康康结果

截屏2021-04-09 下午3.11.43.png

没有问题,到此 catch 方法也就实现了。

最后就是 myPromise 实现的全过程完成代码了

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

class myPromise {
    constructor(executor) {
        try {
            executor(this.resolve, this.reject);
        } catch (error) {
            this.reject(error);
        }
    }

    status = PENDING;
    value = undefined;
    reason = undefined;
    successCallback = [];
    failCallback = [];

    resolve = (value) => {
        if (this.status !== PENDING) return;
        this.status = FULFILLED;
        this.value = value;
        while (this.successCallback.length) {
            this.successCallback.shift()();
        }
    }

    reject = (reason) => {
        if (this.status !== PENDING) return;
        this.status = REJECTED;
        this.reason = reason;
        while (this.failCallback.length) {
            this.failCallback.shift()();
        }
    }

    then(successCallback, failCallback) {
        successCallback = successCallback ? successCallback : value => value;
        failCallback = failCallback ? failCallback : reason => {
            throw reason
        };
        let thenPromise = new myPromise((resolve, reject) => {
            if (this.status === FULFILLED) {
                setTimeout(() => {
                    try {
                        let x = successCallback(this.value);
                        argParsing(thenPromise, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            } else if (this.status === REJECTED) {
                setTimeout(() => {
                    try {
                        let x = failCallback(this.reason);
                        argParsing(thenPromise, x, resolve, reject);
                    } catch (error) {
                        reject(error);
                    }
                }, 0);
            } else {
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = successCallback(this.value);
                            argParsing(thenPromise, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let x = failCallback(this.reason);
                            argParsing(thenPromise, x, resolve, reject);
                        } catch (error) {
                            reject(error);
                        }
                    }, 0);
                });
            }
        })
        return thenPromise;
    }

    finally(callback) {
        return this.then((value) => {
            return myPromise.resolve(callback()).then(() => {
                value
            });
        }, (reason) => {
            return myPromise.resolve(callback()).then(() => {
                throw reason;
            });
        });
    }

    catch(failCallback) {
        return this.then(undefined, failCallback);
    }

    static all(array) {
        let result = [];
        let index = 0;
        return new myPromise((resolve, reject) => {
            function add(key, val) {
                result[key] = val;
                index++;
                if (index === array.length) {
                    resolve(result);
                }
            }
            for (let i = 0; i < array.length; i++) {
                let current = array[i];
                if (current instanceof myPromise) {
                    current.then((value) => {
                        add(i, value)
                    }, (reason) => {
                        reject(reason)
                    })
                } else {
                    add(i, array[i])
                }
            }
        })
    }

    static resolve(value) {
        if (value instanceof myPromise) {
            return value;
        }
        return new myPromise((resolve, reject) => {
            resolve(value);
        })
    }
}

function argParsing(thenPromise, x, resolve, reject) {
    if (thenPromise === x) return reject(new TypeError('Promise 被循环调用了'));
    if (x instanceof myPromise) {
        x.then(resolve, reject);
    } else {
        resolve(x);
    }
}