Promise对象
Promise是一个对象里面保存着某个未来才会结束的事件就是一个异步操作的结果,从它可以获取异步操作的消息。Promise对象的出现就是进行处理各种异步操作,将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。
Promise语法
1.Promise是JS语言中的一个语法,并不是window的成员,所以在前端和后端都可以使用Promise。Promise是一个全局构造函数,它创建出来的对象是一个特殊的数据容器。
Promise对象的三种状态
2.Promise对象代表一个异步操作,数据容器中有三种状态:pending(进行中,表示正在产生结果)、fulfilled(已成功,表示产生了正确的数据)和rejected(已失败,表示产生了错误的数据)。
3.Promise对象的状态改变,只有两种可能:从pending变为fulfilled和从pending变为rejected,只要这两种情况发生,状态就不会再变了,会一直保持这个结果且产生的结果是不可逆的,这时就称为 resolved(已定型)。所以最终Promise对象的状态就是两种结果之一,fulfilled或者rejected,不是成功就是失败。
Promise对象的基本用法
4.new Promise创建对象需要传入参数,参数是回调函数。回调函数需要传入两个参数,这两个参数也是函数,分别是:resolve(success,成功),reject(error,失败)。resolve函数的作用是,将Promise对象的状态从pending 变为fulfilled,在异步操作成功时调用,并将异步操作的结果传递出去。reject函数的作用是,将Promise对象的状态从pending变为rejected,在异步操作失败时调用,并将异步操作报出的错误传递出去等同于抛出错误。因为Promise对象底层封装了try-catch语法,所以当Promise对象内部异步操作失败了程序就会抛出这个错误。但是Promise对象抛出的错误不会传递到外层代码,即不会影响到Promise对象外部代码的执行,它不会让程序卡死在此处。
let p1=new Promise((resolve,reject)=>{
let data={code:404,info:"假设是网络请求后,业务生成的数据"};
if(data.code==404){
reject(data.info);
}else if(data.code==27){
resolve(data.info)
};
});
console.log("程序正常执行1");
p1.then((data)=>{
console.log(data,111);
});
console.log("程序不会卡死2");
因为Promise对象的状态一旦发生了改变是不可逆的,所以也就不会出现即调用了resolve函数又调用了reject函数,则如果Promise对象状态已经变成fulfilled,再抛出错误也是无效的。
let p1=new Promise((resolve,reject)=>{
let data={code:27,info:"假设是网络请求后,业务生成的数据"};
if(data.code==404){
reject(data.info);
}else if(data.code==27){
resolve(data.info);
throw new Error("抛出的一个错误Error对象");
};
});
p1.then((data)=>{
console.log(data,111);
}).catch((err)=>{
console.log(err,222);
});
5.Promise对象的then方法作用是为了取出Promise对象内部状态改变后产生的数据。then方法有两个参数,参数是回调函数,第1个参数是fulfilled状态的回调函数,接收Promise对象传出的异步操作正确的结果,第2个参数(可选)是rejected状态的回调函数,接收Promise对象传出的异步操作错误的结果,可以采用catch方法捕获这个错误。只有当Promise对象的状态发生改变了,才会触发then方法绑定的回调函数。
6.Promise对象的catch方法底层代码就是用JS基础语法中的try-cach封装出来的,catch方法用于捕获发生错误时的数据结果。如果异步操作抛出错误,状态就会变为rejected,就会调用catch()方法传入的回调函数,处理这个错误。另外,then()方法传入的回调函数,如果运行中抛出错误,也会被catch()方法捕获。
7.Promise对象的错误具有“冒泡”性质,会一直向后传递,直到被捕获为止。也就是说,错误总是会被最后一个catch语句捕获。所以then()方法里面可以不定义rejected状态的回调函数(即then的第2个参数),总是使用catch方法。
then函数的返回值和链式调用then函数
8.then函数的返回值由传入then函数的回调函数的返回值决定,传入的回调函数的返回值是Promise对象那么then函数的返回值就是这个Promise对象,反之就把返回值(无论是基本数据还是引用数据的数组,对象,函数等)封装为一个Promise对象作为then函数的返回值。
9.链式调用then函数的原因是then方法返回的是一个新的Promise对象,需要注意的是返回的Promise对象并不是原来的那个Promise对象。因此可以采用链式写法,即then函数后面再调用另一个then函数,而后面调用的这个then函数是前一个then函数返回的新的Promise对象在调用,依此类推就是链式调用了。
Promise对象的静态方法
1.Promise.resolve(),将传入的数据转为一个新的Promise对象,且状态为fulfilled。等价于new Promise((resolve,reject)=>{ resolve()})。Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise对象。
2.Promise.reject(),将传入的数据转换为一个新的Promise对象,该对象的状态为rejected。等价于new Promise((resolve,reject)=>{ reject()})。Promise.reject()方法允许调用时不带参数,直接返回一个resolved状态的 Promise对象。
3.Promise.all([p1, p2, p3]),方法接受一个数组作为参数,p1、p2、p3都是 Promise对象,如果不是Promise对象就会先调用Promise.resolve方法,将参数转为Promise对象,再进一步处理。另外,Promise.all()方法的参数可以不是数组,但必须具有Iterator接口,且该方法返回的每个成员都是Promise对象。
let1 p = Promise.all([p1, p2, p3]);
p的状态由p1、p2、p3决定,分成两种情况。
1.只有p1、p2、p3的状态都变成fulfilled,p的状态才会变成fulfilled,此时p1、p2、p3的返回值组成一个数组,传递给p的回调函数,就是让p调用then方法取出返回的数组。
2.只要p1、p2、p3之中有一个是rejected,p的状态就变成rejected,此时第一个被rejected的的返回值就会传递给p的then方法的第2个参数取出或者catch方法捕获。
关于Promise对象调用的顺序
Promise对象的then方法是一个异步函数,传入then方法的回调函数是异步非阻塞函数,它不会立即被调用执行而是在子线程中等待,当then函数的异步任务完成时才会回到主线程被调用不会阻塞它后面代码的执行。而new Promise创建对象传入的回调函数是异步阻塞函数,这个函数是同步的会立即被调用阻塞它后面代码的执行,但是这个函数内部也可以再次执行异步的任务。
事件循环
异步任务分为:异步宏任务计时器等和异步微任务Promise对象的then方法等。任务排队的队列就是指开启了多个异步任务而子线程的回调函数都需要回到主线程执行时但是js语言是单线程语言又不可能同时执行,就需要根据任务队列的排队顺序按照先入先出的规则执行。
异步任务优先级:异步宏任务优先级高于异步微任务。 在事件循环中先执行异步微任务的原因是事件是一轮一轮循环的,而先执行的异步微任务是上一轮的异步宏任务中的,当上一轮的异步微任务执行完毕以后才会开启下一轮的异步宏任务。
事件循环:从script脚本标签就是一个宏任务开始,同步任务在主线程中直接被执行,遇到异步任务时,异步任务会进入任务队列并注册相应的回调函数, 遇到微任务进入微任务队列,遇到宏任务进入宏任务的队列,待同步任务执行完时,依次执行异步微任务队列中的已经完成了异步任务需要回到主线程调用回调函数的微任务,执行完微任务队列需要回到主线程的微任务就会进入宏任务队列开启下一轮的宏任务。下一轮的宏任务先执行同步任务,遇到异步任务时又添加到任务队列,当同步任务执行完毕时,开始执行这一轮的微任务队列中的微任务,执行完毕开启新的宏任务。即宏任务先运行,宏任务中先运行同步任务,再运行微任务,微任务中也是先运行同步任务再运行微任务,最后当微任务队列需要回到主线程的执行完毕以后就开启下一轮宏任务如此事件就循环起来了。
例题:
<script>
//第1轮宏任务开始
setTimeout(() => {//第2轮宏任务
console.log(0);//第2轮同步任务结束,也只有同步任务
});
new Promise(resolve => {
console.log(1);
setTimeout(() => {//第3轮宏任务
resolve();
var p1 = new Promise((n1, n2) => {
n1(20)
})
p1.then(() => console.log(2));//第3轮宏任务添加的异步微任务1
console.log(3);//第3轮同步任务结束
setTimeout(() => {//第4轮宏任务
console.log(9)//第4轮同步任务结束,也只有同步任务
}, 0)
});
new Promise((n1, n2) => {
n1(20)
}).then(() => console.log(4));//第1轮宏任务添加的异步微任务1
}).then(() => {//第1轮宏任务添加的异步微任务2
console.log(5);
var p2 = new Promise((n1, n2) => {
n1(20)
})
p2.then(() => console.log(8));//第3轮宏任务添加的异步微任务2
setTimeout(() => console.log(6));//第5轮宏任务
});//第5轮同步任务结束,也只有同步任务
console.log(7);//第1轮同步任务结束
//1 7 4 0 3 5 2 8 9 6
</script>