2023年了,你还在为Promise头疼?

  • 阿里云国际版折扣https://www.yundadi.com

  • 阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6

    目录

    Promise初识

    executor函数

    Promise的API

    Promise.prototype.then方法

    Promise.prototype.catch方法

    Promise.reject方法

    Promise.resolve方法

    Promise.all方法

    Promise.race方法

    then的链式调用

    几个关于promise的问题

    async/await

    宏任务与微任务


    Promise初识

    Promise是异步编程的一种新的解决方案以往都是用纯回调函数它其实是一个构造函数所以使用时要new出一个promise实例对象来;

    通俗点讲即Promise是一个构造函数不是一个回调函数;它可以用来封装一个异步操作并获取其结果~

    同步回调与异步回调概念

           回调程序员定义的程序员没有调用但是最终执行了;

           同步回调立即在主线程上执行的不会放到回调队列里;如数组方法相关的回调函数/promise的执行器函数executor函数

           异步回调不会立即执行放入回调队列中执行;如定时器回调/ajax回调/promise成功失败的回调

    Promise实例对象有三种状态分别是pending进行中、fullfilled成功以及rejected失败new出来的Promise实例初始状态均为pending;

    Promise状态只能改变一次即只能从pending改为fullfilled或从pending改为rejected;

    new一个Promise

        <script>
            const test = new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve(200)
                    console.log('你是谁');
                }, 2000)
            })
        </script>

    Promise构造函数会接受一个参数executor函数下面讲解下该函数;

    executor函数

    executor函数会接收两个参数这两个参数均是函数分别用形参resolve、reject表示;

    • 调用resolve会让Promise实例状态变为成功fullfilled同时指定成功的value;
    • 调用reject会让Promise实例状态变为失败rejected同时指定失败的reason;

    注意new出promise实例的同时该函数同步调用立即在主线程上执行。即

    (resolve,reject) => {  }

    是同步回调;但是executor函数的回调中即{ }会指定异步代码;

    Promise的API

    Promise是一个构造函数在原型上存在then和catch等方法自身存在resolve、reject、all、race方法;

    Promise.prototype.then方法

    then方法返回一个新的Promise实例对象它接收两个参数一个是成功的参数value一个是失败的原因error这两个参数都是函数;

    Promise.实例.then(onFullfilled, onRejected) 
    // onFullfilled是成功的回调函数(value) => { }
    // onRejected是失败的回调函数error=> { }

    then方法只有在promise实例状态发生改变后才会被调用异步回调;

    会返回一个新的Promise实例对象它的状态和值由then()所指定的回调函数执行的结果决定;

    如果then所指定的回调返回的是非Promise值a则新的promise状态为fullfilled成功的value值为a

    如果then所指定的回调返回的是Promise实例p则新的promise状态和值与p的状态/值保持一致;

    如果then所指定的回调抛出异常则新的promise实例状态为rejectedreason为抛出的那个异常;

        <script>
            const p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('a')
                }, 1000)
            })
            const x = p.then(
                value => { console.log('成功了1', value); return 999},
                reason => {console.log('失败了1', reason);}
            )
            x.then(
                value => { console.log('成功了2', value)},
                reason => {console.log('失败了1', reason);}
            )
            // 控制台上输出结果 成功了1 a 成功了2 999
        </script>
        <script>
            const p = new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('a')
                }, 1000)
            })
            const x = p.then(
                value => { console.log('成功了1', value); return Promise.reject(999)},
                reason => {console.log('失败了1', reason);}
            )
            x.then(
                value => { console.log('成功了2', value)},
                reason => {console.log('失败了2', reason);}
            )
            // 控制台上输出结果 成功了1 a 失败了2 999
        </script>

    Promise.prototype.catch方法

    Promise.实例.catch(onRejected) 
    // onRejected是失败的回调函数error=> { }

    该方法其实是上述then方法的语法糖相当于

    Promise.实例.then(undefined1, onRejected) 

    Promise.reject方法

    Promise.reject(reason) 

    该方法用于快速返回一个状态必为rejected的Promise实例对象不管传入reason的值是Promise实例对象还是非Promise实例对象结果都是一样;

    const test = Promise.reject(900)
    console.log(test); // Promise {<rejected>: 900}
        <script>
            const test = Promise.reject(900)
            console.log(test); // Promise {<rejected>: 900}
            const test1 = Promise.reject(test) // test是个失败的promise对象
            test1.then(
                value => {console.log('成功了', value) }, 
                reason => {console.log('失败了',reason);} // 失败了 Promise {<rejected>: 900}
            )
        </script>
        <script>
            const test = Promise.resolve(900)
            console.log(test); // Promise {<fulfilled>: 900}
            const test1 = Promise.reject(test) // test是个成功的promise对象
            test1.then(
                value => {console.log('成功了', value) }, 
                reason => {console.log('失败了',reason);} // 失败了 Promise {<rejected>: 900}
            )
        </script>

    Promise.resolve方法

    Promise.resolve(value) 

    该方法用于快速返回一个状态为fullfilled或rejected的Promise实例对象,此处value的值可以是非Promise实例对象或Promise实例对象;

    若是非Promise实例对象(包括undefined以及null等)则返回的一定是个状态为fullfilled的Promise实例对象;

    const test = Promise.resolve(900)
    test.then(
       value => {console.log('成功了', value) }, // 成功了 900
       reason => {console.log('失败了',reason);}
    )

    若是Promise实例对象则分情况

    • 若传入的是成功的Promise对象则返回的也是fullfilled状态的Promise实例对象
        <script>
            const test = Promise.resolve(900)
            console.log(test); // Promise {<fulfilled>: 900}
            const test1 = Promise.resolve(test) // test是个成功的promise对象
            test1.then(
                value => {console.log('成功了', value) }, // 成功了 900
                reason => {console.log('失败了',reason);}
            )
        </script>
    • 若传入的是失败的Promise对象则返回的是rejected状态的Promise实例对象
        <script>
            const test = Promise.reject(900)
            console.log(test); // Promise {<rejected>: 900}
            const test1 = Promise.resolve(test) // test是个失败的promise对象
            test1.then(
                value => {console.log('成功了', value) }, 
                reason => {console.log('失败了',reason);} // 失败了 900
            )
        </script>

    Promise.all方法

    Promise.all(promiseArr) 

    该方法接收一个由Promise实例对象组成的数组做参数返回一个新的Promise实例当数组中所有的Promise实例对象均为fullfilled状态时返回的新的Promise实例对象状态才为fullfilled否则为rejected状态;若返回成功则返回值是由每个Promise实例对象结果组成的数组若返回失败则返回值是该失败的Promise实例对象的结果;

         <script>
            const test1 = new Promise((resolve, reject)=> {
                setTimeout(() => {
                    console.log('test1');
                    resolve(200)
                },1000)
            })
            const test2 = new Promise((resolve, reject)=> {
                setTimeout(() => {
                    console.log('test2');
                    resolve(300)
                },2000)
            })
            const test3 = new Promise((resolve, reject)=> {
                setTimeout(() => {
                    console.log('test3');
                    resolve(400)
                },2000)
            })
            const test4 = Promise.all([test1, test2, test3])
            test4.then(
                value => { console.log('成功了', value); }, //  成功了 [200, 300, 400]
                reason => { console.log('失败了',reason); }
            )
        </script>
         <script>
            const test1 = new Promise((resolve, reject)=> {
                setTimeout(() => {
                    console.log('test1');
                    resolve(200)
                },1000)
            })
            const test2 = new Promise((resolve, reject)=> {
                setTimeout(() => {
                    console.log('test2');
                    reject(300)
                },2000)
            })
            const test3 = new Promise((resolve, reject)=> {
                setTimeout(() => {
                    console.log('test3');
                    reject(400)
                    console.log('test5');
                },2000)
            })
            const test4 = Promise.all([test1, test2, test3])
            test4.then(
                value => { console.log('成功了', value); },
                reason => { console.log('失败了',reason); }// 失败了 300
            )
        </script>

    上述代码依次在控制台上输出

    可以结合后面宏队列微队列进行分析

    •  test1/test2/test3定时器依次放入宏队列中;
    • test4等待;
    • test4.then回调放到test4自身等待test4状态改变后将回调函数推入微队列;
    • 执行test1输出test1test1实例状态成功;
    • 执行test2输出test2test2实例状态失败;
    • 此时test4由于promise.all状态改变了失败则then回调推入微队列;
    • 执行then微队列输出’失败了‘ 300
    • 执行test3定时器依次输出test3、test5并将状态置为失败

    Promise.race方法

    该方法同上参数也是一个由Promise实例对象组成的数组返回一个新的Promise实例对象状态和结果取决于最先完成状态转变的那个Promise实例对象的状态和结果;

    Promise.race(promiseArr) 
        <script>
            const test1 = new Promise((resolve, reject)=> {
                setTimeout(() => {
                    console.log('test1');
                    resolve(200)
                },1000)
            })
            const test2 = new Promise((resolve, reject)=> {
                setTimeout(() => {
                    console.log('test2');
                    reject(300)
                },2000)
            })
            const test3 = Promise.race([test1, test2])
            test3.then(
                value => { console.log('成功了', value); }, // 成功了 200
                reason => { console.log('失败了',reason); }
            )
        </script>
        <script>
            const test1 = new Promise((resolve, reject)=> {
                setTimeout(() => {
                    console.log('test1');
                    resolve(200)
                },1000)
            })
            const test2 = new Promise((resolve, reject)=> {
                setTimeout(() => {
                    console.log('test2');
                    reject(300)
                },200)
            })
            const test3 = Promise.race([test1, test2])
            test3.then(
                value => { console.log('成功了', value); },
                reason => { console.log('失败了',reason);  } // 失败了 300
            )
        </script>

    then的链式调用

    then的链式调用可以串联起多个异步任务使用上篇总结中封装的axios发送请求

    <script>
            import request from 'request' // 引入封装好的axios请求
    
            // 将异步操作封装为promise以便链式调用
            function sendRequest(url, data) {
                return new Promise((resolve, reject) => {
                    const result = request({url: url, data: data}) // 该处是接口请求根据具体情况进行改动
                    if (result.code === 200) {
                        resolve(result.data) // 将成功的数据返回出去给后面成功的value使用
                    } else {
                        reject('接口报错了') // 返回的报错信息根据接口返回报错信息进行修改
                    }
                })
            }
    
            sendRequest('url1', data1)
            .then(value => {
                console.log('第一次请求成功了', value); // 此处value值就是上面resolve传过来的result.data
                return sendRequest('url2', data2); // 成功了则发送第二次请求
            
    }, reason => {
                console.log('第一次请求失败了', reason); // 此处value值就是上面resolve传过来的result.data
            }).then(
                value => {
                console.log('第二次请求成功了', value); // 此处value值就是上面resolve传过来的result.data
                return sendRequest('url3', data3); // 成功了则发送第三次请求
    // 成功了则发送第二次请求这个地方一定要return出去因为then回调中若返回非promise值则会包装成 成功的promise 若返回promise实例如此处的sendRequest('url2', data2)则结果与该实例保持一致若不加return的话则此处的箭头函数返回值就是undefined,则就都会走成功的回调了
            }, reason => {
                console.log('第二次请求失败了', reason); // 此处value值就是上面resolve传过来的result.data
            }).then(
                value => {
                console.log('第三次请求成功了', value); // 此处value值就是上面resolve传过来的result.data
            }, reason => {
                console.log('第三次请求失败了', reason); // 此处value值就是上面resolve传过来的result.data
            })
            
    </script>

    在使用链式调用时可以不写每次的错误调用直接在后面加上.catch方法捕捉报错此时只要任何一个.then方法报错后面的then都不会再被执行这种叫错误穿透

    <script>
            import request from 'request' // 引入封装好的axios请求
    
            // 将异步操作封装为promise以便链式调用
            function sendRequest(url, data) {
                return new Promise((resolve, reject) => {
                    const result = request({url: url, data: data}) // 该处是接口请求根据具体情况进行改动
                    if (result.code === 200) {
                        resolve(result.data) // 将成功的数据返回出去给后面成功的value使用
                    } else {
                        reject('接口报错了') // 返回的报错信息根据接口返回报错信息进行修改
                    }
                })
            }
    
            sendRequest('url1', data1)
            .then(value => {
                console.log('第一次请求成功了', value); // 此处value值就是上面resolve传过来的result.data
                return sendRequest('url2', data2); 
    // 成功了则发送第二次请求这个地方一定要return出去因为then回调中若返回非promise值则会包装成 成功的promise 若返回promise实例如此处的sendRequest('url2', data2)则结果与该实例保持一致若不加return的话则此处的箭头函数返回值就是undefined,则就都会走成功的回调了
            }).then(
                value => {
                console.log('第二次请求成功了', value); // 此处value值就是上面resolve传过来的result.data
                return sendRequest('url3', data3); // 成功了则发送第三次请求
            }).then(
                value => {
                console.log('第三次请求成功了', value); // 此处value值就是上面resolve传过来的result.data
            }).catch(error => {
                console.log('失败了', error);
            })
            
    </script>

    几个关于promise的问题

    如下几个问题是根据尚硅谷天禹老师梳理~感谢🙏

    • 如何改变promise实例的状态?

    1执行resolve(value)从pending变为fullfilled;

    2执行reject(reason)从pending变为rejected;

    3执行器函数executor抛出异常则从pending变为rejected

        <script>
            const test1 = new Promise((resolve, reject)=> {
                throw 900
            })
            test1.then(
                value => { console.log('成功了', value); },
                reason => { console.log('失败了',reason); } // 失败了 900
            )
        </script>
    •  改变promise实例的状态和指定回调函数谁先谁后?

    1正常情况下是先指定回调再改变状态这种情况下回调函数先放到Promise实例对象上等状态改变后再将回调函数推入微队列执行;

    2但是也可以先改变状态再指定回调如延迟一会再调用回调定时器里面加回调函数;

        <script>
            // 先指定回调再改变状态
            const test1 = new Promise((resolve, reject)=> {
                setTimeout(() => {
                    resolve(200)
                },1000)
            })
            test1.then(
                value => { console.log('成功了', value); },
                reason => { console.log('失败了',reason); }
            )
        </script>
        <script>
            // 先改变状态再指定回调
            const test1 = new Promise((resolve, reject) => {
                resolve(200)
            })
            setTimeout(() => {
                test1.then(
                    value => { console.log('成功了', value); },
                    reason => { console.log('失败了', reason); }
                )
            }, 1000)
        </script>
    • 如何中断Promise链?

    当使用then的链式调用时如果某个链状态变为rejected不再执行后面的then回调函数;此种情况可以在失败的回调函数中返回一个pending状态的Promise实例

    <script>
            import request from 'request' // 引入封装好的axios请求
    
            // 将异步操作封装为promise以便链式调用
            function sendRequest(url, data) {
                return new Promise((resolve, reject) => {
                    const result = request({url: url, data: data}) // 该处是接口请求根据具体情况进行改动
                    if (result.code === 200) {
                        resolve(result.data) // 将成功的数据返回出去给后面成功的value使用
                    } else {
                        reject('接口报错了') // 返回的报错信息根据接口返回报错信息进行修改
                    }
                })
            }
    
            sendRequest('url1', data1)
            .then(value => {
                console.log('第一次请求成功了', value); // 此处value值就是上面resolve传过来的result.data
                return sendRequest('url2', data2);
            }, reason => {
                console.log('第一次请求失败了', reason); 
                return new Promise((resolve, reject) => {}) // 返回一个pending状态的promise
            }).then(
                value => {
                console.log('第二次请求成功了', value); // 此处value值就是上面resolve传过来的result.data
                return sendRequest('url3', data3); // 成功了则发送第三次请求
            }, reason => {
                console.log('第二次请求失败了', reason); 
            }).then(
                value => {
                console.log('第三次请求成功了', value); // 此处value值就是上面resolve传过来的result.data
            }, reason => {
                console.log('第三次请求失败了', reason);
            })
            
    </script>

    async/await

    该方法是处理异步请求的终极法宝使用它可避免promise的then链式调用底层还是用的then链式调用但是我们看不到喽仅用几行代码即可实现同样的功能;

    await右侧的表达式一般为Promise实例对象但也可以是其它的值;

    • 若是其它非promise值则直接将此值作为await的返回值;
    • 若是Promise实例对象则返回的是该实例成功后的值;

    await必须写在async函数中但反之async函数中可以没有await但是没有意义哈哈哈;

    若await右侧的表达式执行失败则会抛出异常通常配合try...catch使用;

    该方法虽然表面上看不见任何回调函数但其实程序执行时会将其转变为回调函数执行;

        <script>
            // await/async写法依次输出ab
            const test = new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('a')
                }, 2000)
            })
            async function demo() {
                const test1 = await test
                console.log(test1);
                console.log('b');
            }
            demo()
        </script>
        <script>
            // 后台转换依次输出ab
            const test = new Promise((resolve, reject) => {
                setTimeout(() => {
                    resolve('a')
                }, 2000)
            })
            test.then((test1) => {
                console.log(test1);
                console.log('b');
            })
        </script>

    宏任务与微任务

    又称为宏队列与微队列其中除了promise的then回调外大部分都要被推入宏队列在执行宏队列时要先检查微队列中是否有任务如有则要先执行微任务没有才依次执行宏任务

        <script>
            // 依次输出 主线程 2 3 1
            setTimeout(() => {
                console.log(1);
            }, 1000)
            Promise.resolve(2).then(
                value => {console.log(value);}
            )
            Promise.resolve(3).then(
                value => {console.log(value);}
            )
            console.log('主线程');
        </script>

    总结promise基本的编码流程

    • 创建Promise的实例对象此时为pending状态传入executor函数
    • 在executor函数中开启异步任务定时器/ajax请求/axios请求
    • 根据异步任务的结果做不同的处理; 
    1.    若异步任务成功了则调用resolve(value)让promise实例对象状态变为成功(fullfilled) 同时指定成功的value;
    2.    若异步任务失败了则调用reject(reason)让promise实例对象状态变为失败(rejected) 同时指定成功的reason;
    • 通过then方法为promise指定成功/失败的回调函数来获取成功的value/失败的reasonthen方法指定的成功/失败的回调都是异步回调

  • 阿里云国际版折扣https://www.yundadi.com

  • 阿里云国际,腾讯云国际,低至75折。AWS 93折 免费开户实名账号 代冲值 优惠多多 微信号:monov8 飞机:@monov6