js- 彻底整明白 Promise [含手写代码] !

55 阅读9分钟

一、基本使用

Promise是一个构造函数, 该对象用来封装一个 异步操作 并可以获取其成功/失败的结果值.

Promise的状态(PromiseState): pending 等待 | resolved、fulfilled 成功 | rejected 失败

一个Promise对象只能改变一次状态pending -> resolved || pending -> rejected.Promise的值(PromiseResult): 保存着异步任务【成功、失败】的结果.无论是状态还是实例对象的值 改变都是 调用 resolvereject方法所导致的.

Promise的执行器(实例对象里面参数箭头函数)是同步立即执行的!

Promise实例对象原型上thencatchfinally方法

    const p = new Promise((resolve, reject) => {
            let num = Math.floor(Math.random() * 100 + 1);
            if (num <= 30) {
                resolve(num);
            } else {
                reject(num)
            }
        })
        // p.then((num) => {
        //     alert(`恭喜您中奖了,中奖数字为${num}`)
        // }, (num) => {
        //     alert(`很遗憾再接再厉,数字为${num}`)
        // })
        p.then(value => alert(`恭喜您中奖了,中奖数字为${value}`)).catch(reason => alert(`很遗憾再接再厉,数字为${reason}`))

二、静态方法

2.1 resolve

  1. resolve参数为非Promise类型的对象,则返回结果为成功的promise对象
        let p1 = Promise.resolve(521);
        console.log(p1);
  1. 参数为Promise类型的对象,则返回结果由执行器内 看是成功调用还是失败调用导致结果
         let p2 = Promise.resolve(new Promise((resolve, reject) => {
            // 执行器会同步立即执行
            // resolve('ok')
            reject('error');
        }))
        p2.catch(err => console.log(err))
        console.log(p2); // 如果是错误的返回结果我们不处理浏览器会报错Uncaught (in promise) error

2.2 reject

无论参数是啥其状态都是rejected

        let p3 = Promise.reject(521);
        let p4 = Promise.reject(new Promise((resolve, reject) => {
            throw reject('失败');
        }));
        p3.catch(err => console.log(err))
        p4.catch(err => console.log(err))
        console.log(p3);
        console.log(p4);

2.3 all

只要数组中有一个失败,结果返回失败的Promise

        let p1 = new Promise((resolve, reject) => {
            resolve('ok')
        });
        // let p2 = Promise.resolve('xxxx');
        let p2 = Promise.reject('error');
       
        const result = Promise.all([p1, p2]);
        console.log(result);
        console.log(result.then(res => console.log(res)));
        console.log(result.catch(err => console.log(err)));

2.4 race

只要数组中哪个先成功 结果返回那个,一旦有失败的结果返回失败

        let p1 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('ok')
            }, 1000)
        });
        let p2 = Promise.resolve('xxxx');
        // let p2 = Promise.reject('error');
        let p3 = Promise.resolve('yyyy');
        const result = Promise.race([p1, p2, p3]);
        console.log(result);

三、链式调用

        let p = new Promise((resolve, reject) => {
            resolve('ok');
        })
        let result = p.then(value => {
            console.log(value);
            return new Promise((resolve, reject) => {
                resolve('success');
            })
        }).then(value => {
            console.log(value);
            return new Promise((resolve, reject) => {
                reject('error');
            })
        })
            .then(value => {
                console.log(value);
            }, reason => {
                console.log(reason);
            })
            .then(value => {
                console.log(value); // undefined
                return 521
            })
        console.log(result);
Snipaste_2023-04-25_16-13-44.jpg

结论:

  1. result 的 结果由最后一个then决定
  2. 每个打印的结果 要去上一级的then方法中 看返回的Promise 调用的是谁 决定了 下一个then走哪个回调函数
  3. 当上一级没有返回Promise , 结果为undefined 我们可知then方法的结果是由 回调函数返回的东西决定的

异常穿透

我们在上面例子中 将 reject('error')的下一级then 的第二个回调去掉,这样会发生什么?

        let p = new Promise((resolve, reject) => {
            resolve('ok');
        })
        let result = p.then(value => {
            console.log(value);
            return new Promise((resolve, reject) => {
                resolve('success');
            })
        }).then(value => {
            console.log(value);
            return new Promise((resolve, reject) => {
                reject('error');
            })
        })
            .then(value => {
                console.log(1111);
                console.log(value);
                return new Promise((resolve, reject) => {
                    resolve('xxxx');
                })
            })
            .then(value => {
                console.log(value);
                return new Promise((resolve, reject) => {
                    resolve('yyyy');
                })  
            })
            .catch(reason => {
                console.log(reason);
            })
        console.log(result);
xy1.jpg

只要是执行器中出现 reject 函数的调用 ,下一级以及之后的then方法的第一回调就进不去了,直接会走进最后catch方法

如何中断Promise链

在需要中断的上一级直接then方法中 return new Promise(()=>{});

     let p = new Promise((resolve, reject) => {
            resolve('ok');
        })
        p.then(value => {
            console.log(value); // ok
        }).then(() => {
            console.log(111111); // 111111
            return new Promise(()=>{});
        }).then(() => {
            console.log(222222);
        }).then(() => {
            console.log(333333);
        }).catch(reason => {
            console.log(reason);
        })

四、async、await

解决回调地狱:

async 用于申明一个异步函数 :

  • 异步函数的内部代码执行过程和普通的函数是一致的,默认情况下也是会被同步执行

  • 异步函数的返回值特点:

    1. 明确有返回一个普通值,相当于Promise.resolve(返回值)
    2. 明确返回一个promise,则由这个promise决定

await 关键字

  • 通常await关键字后面都是跟一个Promise
  • 这个promise状态变为fulfilled才会执行await后续的代码,所以await后面的代码,相当于包括在.then方法的回调中,如果状态变为rejected,你则需要在函数内部try catch,或者进行链式调用进行.catch操作
         async function Fn() {
            let p = new Promise((resolve, reject) => {
                resolve('ok');
                // reject('error');
            })
            // 捕获错误
            try {
                let res = await p;
                console.log(res);
            } catch (err) {
                console.log(err);
            }
        }
        Fn();

五、手写promise整起来😊

5.1 基础版

通过class定义一个MyPromise, new实例时传入executor执行器函数,立即执行。同时将等待状态pending变为fulfilledrejected

  1. 执行器有两个参数resolvereject都为函数用来改变状态,将成功的值或失败的原因保存起来。
  2. 通过then方法 获取结果,在原型对象then方法中判断状态分别调用回调函数,并传出保存好的结果。
  3. 状态只能由等待->成功或者等待->失败,需要在resolvereject函数中进行判断,状态不符合程序return阻止向下进行
  4. try...catch 捕获立即执行的执行器函数
        const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected';
        class MyPromise {
            constructor(executor) {
                // 初始state状态为等待
                this.status = PENDING;
                // 成功之后的值
                this.value = undefined;
                // 失败后的原因
                this.reason = undefined;
                let resolve = value => {
                    if (this.status !== PENDING) return;
                    this.status = FULFILLED;
                    this.value = value
                }
                let reject = reason => {
                    if (this.status !== PENDING) return;
                    this.status = REJECTED;
                    this.reason = reason
                }
                // 如果executor执行报错,直接执行reject
                try {
                    executor(resolve, reject);
                } catch (err) {
                    reject(err);
                }
            }
            then(onFulfilled, onRejected) {
                if (this.status === FULFILLED) {
                    onFulfilled(this.value)
                } else if (this.status === REJECTED) {
                    onRejected(this.reason)
                }
            }
        }

        // 测试1
        let p = new MyPromise((resolve, reject) => {
            // resolve('成功')
            reject('失败')
        }).then((value) => {
            console.log(value);
        }, (reason) => {
            console.log(reason);
        })

5.2 解决异步调用resolve、reject和多实例then问题

现在基本可以实现简单的同步代码,但是当resolve在setTomeout内执行,then时status还是pending等待状态 我们就需要在then调用的时候,将成功和失败存到各自的数组,一旦reject或者resolve,就调用它们

        const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected';
        class MyPromise {
            constructor(executor) {
                // 代码省罗...
                // 成功回调
                this.successCallback = [];
                // 失败回调
                this.failCallback = [];
                let resolve = value => {
                    if (this.status !== PENDING) return;
                    this.status = FULFILLED;
                    this.value = value;
                    // 执行成功回调
                    while (this.successCallback.length) this.successCallback.shift()(this.value)
                }
                let reject = reason => {
                    if (this.status !== PENDING) return;
                    this.status = REJECTED;
                    this.reason = reason
                    // 执行失败回调
                    while (this.failCallback.length) this.failCallback.shift()(this.reason)
                }
              // 代码省罗...
            }
            then(onFulfilled, onRejected) {
                if (this.status === FULFILLED) {
                    onFulfilled(this.value)
                } else if (this.status === REJECTED) {
                    onRejected(this.reason)
                } else {
                    // 异步更改状态时, then函数是立即执行的此时状态为padding,需将成功回调和失败回调存储起来
                    this.successCallback.push(onFulfilled)
                    this.failCallback.push(onRejected)
                }
            }
        }

        // 测试2
        let p = new MyPromise((resolve, reject) => {
            setInterval(() => {
                // resolve('成功。。。')
                reject('失败。。。')
            }, 5000)
        })

        p.then(value => {
            console.log(value);
        }, reason => {
            console.log(reason);
        })

        p.then(value => {
            console.log(value);
        }, reason => {
            console.log(reason);
        })

5.3 解决链式调用

  1. 链式调用 能够.then必然上一级返回promise对象,我们需要在then方法中创建一个promise对象将原有代码包裹,并返回promise对象结果。
  2. 调用then方法传入的回调函数的返回值,需要进行判断
    • 判断 result 的值是普通值还是promise对象
    • 如果是普通值 直接调用resolve
    • 如果是promise对象 查看promsie对象返回的结果
    • 再根据promise对象返回的结果 决定调用resolve 还是调用reject
  3. 为了拿到创建的promise实例 需将代码变为异步代码通过setTimeout
  4. try...catch 捕获回调函数执行的错误
  5. 一旦return 返回的自己实例,我们需要解决环引用问题 x1.jpg 处理环引用 if (promis2 === result) { return reject(new TypeError('Chaining cycle detected for promise #')) }
        const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected';
        class MyPromise {
            constructor(executor) {
               // 代码省罗...
            }
            then(onFulfilled, onRejected) {
                let promis2 = new MyPromise((resolve, reject) => {
                    if (this.status === FULFILLED) {
                        setTimeout(() => {
                            try {
                                let result = onFulfilled(this.value)
                                // 判断 result 的值是普通值还是promise对象
                                // 如果是普通值 直接调用resolve 
                                // 如果是promise对象 查看promsie对象返回的结果 
                                // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                                this.#testPromise(promis2, result, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    } else if (this.status === REJECTED) {
                        setTimeout(() => {
                            try {
                                let result = onRejected(this.reason)
                                this.#testPromise(promis2, result, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    } else {
                        // 异步更改状态时, then函数是立即执行的此时状态为padding,需将成功回调和失败回调存储起来
                        this.successCallback.push(() => {
                            setTimeout(() => {
                                try {
                                    let result = onFulfilled(this.value)
                                    this.#testPromise(promis2, result, resolve, reject)
                                } catch (error) {
                                    reject(error)
                                }
                            }, 0)
                        })
                        this.failCallback.push(() => {
                            setTimeout(() => {
                                try {
                                    let result = onRejected(this.reason)
                                    this.#testPromise(promis2, result, resolve, reject)
                                } catch (error) {
                                    reject(error)
                                }
                            }, 0)
                        })
                    }
                })
                return promis2
            }

            #testPromise(promis2, result, resolve, reject) {
                // 处理环引用
                if (promis2 === result) {
                    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
                }
                if (result instanceof MyPromise) {
                    // promise对象
                    result.then(value => resolve(value), reason => reject(reason))
                } else {
                    // 普通值
                    resolve(result)
                }
            }
        }

        // 测试2
        let p = new MyPromise((resolve, reject) => {
            resolve('成功。。。')
        })
        
        let p1 = p.then(value => {
            console.log(value);
            return new MyPromise((resolve, reject) => {
                // resolve('hahaha')
                reject('yayaya')
            })
        }).then(value => {
            console.log(111111111);
            console.log(value);
        }, reason => {
            console.log(222222222);
            console.log(reason);
        })

5.4 then参数可选

        const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected';
        class MyPromise {
            constructor(executor) {
                // 代码省罗...
            }
            then(onFulfilled, onRejected) {
                onFulfilled = onFulfilled ? onFulfilled : value => value;
                onRejected = onRejected ? onRejected : reason => { throw reason };
                let promis2 = new MyPromise((resolve, reject) => {
                    // 代码省罗...
                })
                return promis2
            }

            #testPromise(promis2, result, resolve, reject) {
              // 代码省罗...
            }
        }

        // 测试2
        let p = new MyPromise((resolve, reject) => {
            reject('成功。。。')
        })

        let p1 = p.then().then(value => {
            console.log(111111111);
            console.log(value);
        }, reason => {
            console.log(222222222);
            console.log(reason);
        })
dggc.gif

完整代码

catch方法通过调用then方法,将第一个参数为undefined,参数二为失败的回调传入即可

               const PENDING = 'pending', FULFILLED = 'fulfilled', REJECTED = 'rejected';
        class MyPromise {
            constructor(executor) {
                // 初始state状态为等待
                this.status = PENDING;
                // 成功之后的值
                this.value = undefined;
                // 失败后的原因
                this.reason = undefined;
                // 成功回调
                this.successCallback = [];
                // 失败回调
                this.failCallback = [];

                let resolve = value => {
                    if (this.status !== PENDING) return;
                    this.status = FULFILLED;
                    this.value = value;
                    // 执行成功回调
                    while (this.successCallback.length) this.successCallback.shift()()
                }
                let reject = reason => {
                    if (this.status !== PENDING) return;
                    this.status = REJECTED;
                    this.reason = reason
                    // 执行失败回调
                    while (this.failCallback.length) this.failCallback.shift()()
                }
                // 如果executor执行报错,直接执行reject
                try {
                    executor(resolve, reject);
                } catch (err) {
                    reject(err);
                }
            }
            then(onFulfilled, onRejected) {
                onFulfilled = onFulfilled ? onFulfilled : value => value;
                onRejected = onRejected ? onRejected : reason => { throw reason };
                let promis2 = new MyPromise((resolve, reject) => {
                    if (this.status === FULFILLED) {
                        setTimeout(() => {
                            try {
                                let result = onFulfilled(this.value)
                                // 判断 result 的值是普通值还是promise对象
                                // 如果是普通值 直接调用resolve 
                                // 如果是promise对象 查看promsie对象返回的结果 
                                // 再根据promise对象返回的结果 决定调用resolve 还是调用reject
                                this.#testPromise(promis2, result, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    } else if (this.status === REJECTED) {
                        setTimeout(() => {
                            try {
                                let result = onRejected(this.reason)
                                this.#testPromise(promis2, result, resolve, reject)
                            } catch (error) {
                                reject(error)
                            }
                        }, 0)
                    } else {
                        // 异步更改状态时, then函数是立即执行的此时状态为padding,需将成功回调和失败回调存储起来
                        this.successCallback.push(() => {
                            setTimeout(() => {
                                try {
                                    let result = onFulfilled(this.value)
                                    this.#testPromise(promis2, result, resolve, reject)
                                } catch (error) {
                                    reject(error)
                                }
                            }, 0)
                        })
                        this.failCallback.push(() => {
                            setTimeout(() => {
                                try {
                                    let result = onRejected(this.reason)
                                    this.#testPromise(promis2, result, resolve, reject)
                                } catch (error) {
                                    reject(error)
                                }
                            }, 0)
                        })
                    }
                })
                return promis2
            }

            catch(onRejected){
                return this.then(undefined, onRejected)
            }
            #testPromise(promis2, result, resolve, reject) {
                // 处理环引用
                if (promis2 === result) {
                    return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
                }
                if (result instanceof MyPromise) {
                    // promise对象
                    result.then(value => resolve(value), reason => reject(reason))
                } else {
                    // 普通值
                    resolve(result)
                }
            }
        }

        // 测试2
        let p = new MyPromise((resolve, reject) => {
            reject('成功。。。')
        })

        let p1 = p.then().then(value => {
            console.log(111111111);
            console.log(value);
        }, reason => {
            console.log(222222222);
            console.log(reason);
        })

手写race、all

     class MyPromise {
            static all(arr) {
                let result = [], count = 0;
                return new Promise((resolve, reject) => {
                    for (let i = 0; i < arr.length; i++) {
                        Promise.resolve(arr[i]).then(res => {
                            // 将执行结果存入result数组
                            result[i] = res
                            count++
                            if (count === arr.length) {
                                resolve(result)
                            }
                        }).catch(reason => {
                            reject(reason)
                        })
                    }
                })
            }

            static race(arr) {
                return new Promise((resolve, reject) => {
                    for (let i = 0; i < arr.length; i++) {
                        Promise.resolve(arr[i]).then(value => {
                            resolve(value)
                        }).catch(reason => {
                            reject(reason)
                        })
                    }
                })
            }
        }

        // 测试代码
        let p1 = new Promise((resolve, reject) => {
            resolve('成功')
        })
        let p2 = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve('1111111111')
                // reject('2222222')
            }, 2000)
        })
        let result1 = MyPromise.all([p1, 111, p2])
        let result2 = MyPromise.race([p1, 111, p2])
        console.log(result1);
        console.log(result2);