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

目录

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实例状态为rejected,reason为抛出的那个异常;

    <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实例对象的结果;all中每个异步操作都是并行执行的,等到它们都执行完后才会进到then;

     <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,输出test1,test1实例状态成功;
  • 执行test2,输出test2,test2实例状态失败;
  • 此时test4由于promise.all状态改变了(失败),则then回调推入微队列;
  • 执行then微队列,输出’失败了‘ 300
  • 执行test3定时器,依次输出test3、test5,并将状态置为失败

Promise.race方法

该方法同上,参数也是一个由Promise实例对象组成的数组,返回一个新的Promise实例对象,状态和结果取决于最先完成状态转变的那个Promise实例对象的状态和结果;与all方法不同的是,谁先执行完成就先执行回调先执行完的不管是进行了race的成功回调还是失败回调,其余的将不会再进入race的任何回调

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写法,依次输出a,b
        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>
        // 后台转换,依次输出a,b
        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>
console.log(1)
setTimeout(function(){
  console.log(2)
}, 0)
new Promise(function(resolve){ //这里的回调是同步的
  console.log(3)
  resolve()
}).then(function(){  //异步微任务
  console.log(4)
})
console.log(5)
//输出结果 1,3,5,4,2

总结promise基本的编码流程:

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

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

迷糊的小小淘

整理不易,赏点动力~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值