• 手写promise


    手写promise

    promise 共有三个状态 定义状态

    const promise_state_pending = 'pending'
    const promise_state_fulfilled = 'fulfilled'
    const promise_state_onRejected = 'onRejected'
    
    • 1
    • 2
    • 3

    promise 是通过new 来生成的对象 它的参数是一个函数对象并且函数中也有两个参数

    class ShkPromise {
    
        constructor(execFn) {
    
            const resolve = (value) => {
                console.log(value) // 此处打印123
            }
            const rejected = (err) => {
                
            }
            execFn(resolve,rejected)
        }
    }
    new ShkPromise((res,rej)=>{
        res(123)
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在确定以res执行后则会执行返回后的对象的then方法

    const promise_state_pending = 'pending'
    const promise_state_fulfilled = 'fulfilled'
    const promise_state_onRejected = 'onRejected'
    
    
    class ShkPromise {
    
        constructor(execFn) {
            this.state = promise_state_pending;
            this.value = null;
            this.reason = null;
            const resolve = (value) => {
                // 此处需将状态保存 保证then调用时可以拿到
                this.value = value;
            }
            const rejected = (err) => {
                this.reason = err;
    
            }
            execFn(resolve, rejected)
        }
    
        then(execThen) {
            execThen(this.value)
    
        }
        carth(execCarth){
            execCarth(this.reason)
        }
    }
    const promise=new ShkPromise((res, rej) => {
        res(123)
    })
    promise.then(res => {
        console.log(res)
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37

    但是此时会有问题 当把then换成 catch 发现它也可以运行catch里面的函数 故需要将状态改掉

    const promise_state_pending = 'pending'
    const promise_state_fulfilled = 'fulfilled'
    const promise_state_onRejected = 'onRejected'
    
    
    class ShkPromise {
    
        constructor(execFn) {
            this.state = promise_state_pending;
            this.value = null;
            this.reason = null;
            const resolve = (value) => {
    
                // 此处需将状态保存 保证then调用时可以拿到
                this.value = value;
                this.state = promise_state_fulfilled;
    
            }
            const rejected = (err) => {
                this.reason = err;
                this.state = promise_state_onRejected;
    
            }
            execFn(resolve, rejected)
        }
    
        then(execThen) {
            if (this.state === promise_state_fulfilled)
                execThen(this.value)
    
        }
    
        catch(execCatch) {
            // 在此处判断状态
            if (this.state === promise_state_onRejected)
                execCatch(this.reason)
        }
    }
    
    const promise = new ShkPromise((res, rej) => {
        rej(123)
    })
    // 此时 .catch 中的方法将不会执行
    promise.catch(res => {
        console.log(res)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46

    promise中then调用其实是在微任务中进行的 此时的then是同步

    • 在resolve函数中创建微任务函数queueMicrotask
    • 将 then方法存入一个数组中 等待 微任务执行时再一起执行
    const promise_state_pending = 'pending'
    const promise_state_fulfilled = 'fulfilled'
    const promise_state_onRejected = 'onRejected'
    
    
    class ShkPromise {
    
        constructor(execFn) {
            this.state = promise_state_pending;
            this.value = null;
            this.reason = null;
            this.successCallbackFn = [];
            this.catchCallbackFn = [];
            const resolve = (value) => {
                // 创建微任务
                queueMicrotask(() => {
                        this.value = value;
                        this.state = promise_state_fulfilled;
                        this.successCallbackFn.forEach(ele => ele(value))
                })
            }
            const rejected = (err) => {
                queueMicrotask(() => {
                    this.reason = err;
                    this.state = promise_state_onRejected;
                    this.catchCallbackFn.forEach(ele => ele(err))
                })
    
    
            }
            execFn(resolve, rejected)
        }
    
        then(execThen) {
                this.successCallbackFn.push(execThen)
    
        }
    
        catch(execCatch) {
            // 在自处判断状态
            if (this.state === promise_state_onRejected)
                execCatch(this.reason)
        }
    }
    
    const promise = new ShkPromise((res, rej) => {
        res(123)
    })
    promise.then(res => {
        console.log(res)
    })
    promise.then(res => {
        console.log(res)
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55

    当then是在setTimeout里时 ,将不会调用 此时需要在then函数中做个判断

      then(execThen) {
            if (this.state === promise_state_pending) {
                this.successCallbackFn.push(execThen)
            }else {
                execThen(this.value)
            }
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    此时需要考虑then的返回值其实也是个promise函数 故需要将 then进行改造

    • push的时候将不能直接将 函数push 得用箭头函数再包裹一层
    • 得有 try catch。当走到 catch时其实会调用 .catch的回调函数
      then(execThen) {
            return new ShkPromise((res, rej) => {
            if (this.state === promise_state_pending) {
                this.successCallbackFn.push(()=>{
                    try {
                        const result = execThen(this.value)
                        res(result);
                    } catch (err) {
                        rej(err)
    
                    }
                })
            }else {
                try {
                    const result =  execThen(this.value)
                    res(result);
                } catch (err) {
                    rej(err)
    
                }
               
            }
            })
    
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25

    其实 then调用是有两个参数的 第一个参数是成功的回调函数 第二个参数是 失败的回调函数 故此时还需要添加第二个参数

       then(execThen, execCatch) {
            return new ShkPromise((res, rej) => {
                // 当在调用微任务之前调用
                if (this.state === promise_state_pending) {
                    // 将成功的回调函数放入数组
                    this.successCallbackFn.push(() => {
                        try {
                            const result = execThen(this.value)
                            res(result);
                        } catch (err) {
                            rej(err)
    
                        }
    
                    })
                    // 失败的
                    this.catchCallbackFn.push(() => {
                        try {
                            const reason = execCatch(this.reason)
                            res(reason);
                        } catch (err) {
                            rej(err)
    
                        }
    
                    })
    
                }
                if (this.state === promise_state_fulfilled) {
                    // 成功的 但是在微任务之后
                    try {
                        const result = execThen(this.value)
                        res(result);
                    } catch (err) {
                        rej(err)
    
                    }
    
                }
                if (this.state === promise_state_onRejected) {
                    // 失败的
                    try {
                        const reason = execCatch(this.value)
                        res(reason);
                    } catch (err) {
                        rej(err)
                    }
                }
            })
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50

    考虑完then 需要考虑catch 其实catch很简单 只需要执行内部的then方法即可

    • catch也是返回一个promise 故也需要返回值
     catch(execCatch) {
            return this.then(undefined, execCatch)
        }
    
    • 1
    • 2
    • 3

    但是我们发现如果直接调用 catch 是没有问题的 但是如果先调用 then(不传第二个参数) 再调用catch就会报错

    const promise = new ShkPromise((res, rej) => {
        rej(123)
    })
    promise.catch(res => {
        console.log(res)// 没问题
    })
    // execCatch is not a function
    
    promise.then(()=>{
        console.log(123)
    }).catch(res => {
    
        console.log(res)
    })
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    这是因为 then 时 传入的第二个参数是undefined 故当执行 undefined()时会报错

    • 在开头对undefined做处理
     then(execThen, execCatch) {
          // 当传入udefined时就将 catch 改成 后面的函数
            execCatch = execCatch ?? (() => {
                throw this.reason
            })
          ... other
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    同理 当then函数中 第一个参数为undefined时肯定也会有问题

     then(execThen, execCatch) {
            execThen = execThen?? (()=>{
                return this.value
            })
          ... other
        }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    此时promise 大功告成 但是当 new时 promise参数中的res和rej都执行时会发现都可以执行

    const promise = new ShkPromise((res, rej) => {
        rej(123)
        res(222)
    })
    
    promise.then((res)=>{
        console.log(res) //222
    }).catch(error => {
        console.log(error,'我是error') //123 我是error
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在前面我们发现在resolve 和 rejected函数执行的时候并没有查看状态

     const resolve = (value) => {
                queueMicrotask(() => {
                  // 若不等于 pending 说明是执行第二次的 res或者 rej
                    if (this.state === promise_state_pending) {
                        this.value = value;
                        this.state = promise_state_fulfilled;
                        this.successCallbackFn.forEach(ele => ele(value))
                    }
                })
    
            }
            const rejected = (err) => {
                queueMicrotask(() => {
                     if (this.state === promise_state_pending) {
                        this.reason = err;
                        this.state = promise_state_onRejected;
                        this.catchCallbackFn.forEach(ele => ele(err))
                    }
                })
            }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    finally

    • 就是在最后的时候执行 ,不管是 成功失败 最后会走finally 故将此回调函数用then调用 并且两参数都是 finally函数
     finally(execFinally){
            this.then(execFinally,execFinally)
        }
    
    
    • 1
    • 2
    • 3
    • 4
    • 若不想将后续的参数传入 finally则将其包裹一层箭头函数
     finally(execFinally){
            this.then(()=>execFinally(),()=>execFinally())
        }
    
    • 1
    • 2
    • 3

    大功告成,但是有个缺点 若 promise最后是失败的状态 不会将错误抛到上一层

    // 报错
    const promise = new Promise((res, rej) => {
        rej(222)
    })
    // 不会报错
    const promise = new ShkPromise((res, rej) => {
        rej(222)
    })
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    以下是完整代码

    const promise_state_pending = 'pending'
    const promise_state_fulfilled = 'fulfilled'
    const promise_state_onRejected = 'onRejected'
    
    
    class ShkPromise {
    
        constructor(execFn) {
            this.state = promise_state_pending;
            this.value = null;
            this.reason = null;
            this.successCallbackFn = [];
            this.catchCallbackFn = [];
            const resolve = (value) => {
                queueMicrotask(() => {
                    if (this.state === promise_state_pending) {
                        this.value = value;
                        this.state = promise_state_fulfilled;
                        this.successCallbackFn.forEach(ele => ele(value))
                    }
                })
    
            }
            const rejected = (err) => {
                queueMicrotask(() => {
                    if (this.state === promise_state_pending) {
                        this.reason = err;
                        this.state = promise_state_onRejected;
                        this.catchCallbackFn.forEach(ele => ele(err))
                    }
                })
            }
            execFn(resolve, rejected)
        }
    
        then(execThen, execCatch) {
            execThen = execThen?? (()=>{
                return this.value
            })
            execCatch = execCatch ?? (() => {
                throw this.reason
            })
            return new ShkPromise((res, rej) => {
                // 当在调用微任务之前调用
                if (this.state === promise_state_pending) {
                    // 将成功的回调函数放入数组
                    this.successCallbackFn.push(() => {
                        try {
                            const result = execThen(this.value)
                            res(result);
                        } catch (err) {
                            rej(err)
    
                        }
    
                    })
                    // 失败的
                    this.catchCallbackFn.push(() => {
                        try {
                            const reason = execCatch(this.reason)
                            res(reason);
                        } catch (err) {
                            rej(err)
    
                        }
    
                    })
    
                }
                if (this.state === promise_state_fulfilled) {
                    // 成功的 但是在微任务之后
                    // 此处代码可复用建议最后提为一个函数 使用函数调用
                    try {
                        const result = execThen(this.value)
                        res(result);
                    } catch (err) {
                        rej(err)
    
                    }
    
                }
                if (this.state === promise_state_onRejected) {
                    // 失败的
                    try {
                        const reason = execCatch(this.value)
                        res(reason);
                    } catch (err) {
                        rej(err)
                    }
                }
            })
        }
        catch(execCatch) {
            return this.then(undefined, execCatch)
        }
        finally(execFinally){
            this.then(()=>execFinally(),()=>execFinally())
        }
    }
    
    
    
    const promise = new ShkPromise((res, rej) => {
        res(123);
    })
    
    
    promise.catch((err) => {
        console.log('err', err)
        return 123
    }).then(res => {
        console.log(res)
        return '999'
    }).then(res => {
        console.log('000', res)
        throw '111'
    }).finally(()=>{
        console.log('w s finally')
    })
    // promise.catch((error) => {
    //     console.log('error', error)
    //     return 123
    // }).then((res) => {
    //     console.log('res', res)
    //     return '124443'
    // }).catch((err) => {
    //     // return 123;
    // }).then(res => {
    //     console.log('res', res)
    //     throw '1232'
    // })
    //     .catch(error => {
    //         console.log(error,'error')
    //     }).catch(()=>{}).then(()=>{throw 213111}).finally((res)=>{
    //     console.log('res222',res)
    //     console.log('999')
    // })
    // setTimeout(() => promise.then(res => console.log(res)))
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
    • 97
    • 98
    • 99
    • 100
    • 101
    • 102
    • 103
    • 104
    • 105
    • 106
    • 107
    • 108
    • 109
    • 110
    • 111
    • 112
    • 113
    • 114
    • 115
    • 116
    • 117
    • 118
    • 119
    • 120
    • 121
    • 122
    • 123
    • 124
    • 125
    • 126
    • 127
    • 128
    • 129
    • 130
    • 131
    • 132
    • 133
    • 134
    • 135
    • 136
    • 137
    • 138
    • 139
  • 相关阅读:
    【HTML5期末大作业】制作一个简单HTML我的班级网页(HTML+CSS+JS)
    掌握这些PDF转Word方法,从此你就是大神的存在
    2022网络搭建国赛题交换机安全配置
    edge 浏览器截取全图保存成图片
    解决charles只能使用30分钟
    Acwing 890. 能被整除的数
    虚惊!差一点痛失美团offer,罪魁祸首就是Redis?
    mysql存储过程
    vue3中实现放大镜效果,使用ref获取节点元素
    计算机毕业设计微信小程序保险管理平台+后台
  • 原文地址:https://blog.csdn.net/weixin_45272209/article/details/126337112