目录
(3) promise 状态和指定的改变,回调函数的执行顺序
1> 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调;
(4)promise.then()返回新的 promise 的结果状态如何决定?
前言
Promise
对象的存在使得我们在处理异步操作的时候写法更加简洁,且结合async
和await
关键字使用的话会让代码更加清晰和优雅。
一.promise是什么?
(1)回调监狱
在学习promise之前,我们需要先来看一下我们以前在JS中是如何处理异步操作的,我们通常会使用回调函数,但回调函数虽然能够解决我们实现异步操作队列化的需求。但当出现一个方法中嵌套着多个回调函数的时候,此时代码的结构就会冗余,也就是出现回调地狱的问题。
然而Promise
对象的产生则能够帮助我们较为优雅地解决多个回调函数嵌套的问题,让我们的JS代码结构更加清晰,维护起来更加方便。
(2)promise定义
Promise 是一种异步编程的解决方案,一般来说,我们封装一个网络请求的函数,因为不能立刻返回结果,所以我们可以将其传入另外一个函数,在数据请求成功时,将数据通过传入的函数回调回去。其实它就是一个构造函数,自己身上有all、reject、resolve这几个方法,原型上有then、catch等方法。
二.为什么要使用promise?
由于ajax和axios请求很重要。通俗来说,可能因为网速的不同,得到的返回值的时间也会不同,但是下一步要执行的代码依赖于上一次请求返回值,只有等结果出来了才知道如何下一步操作。
三.promise的优点
- .Promise 对象提供了简洁的API,使得控制异步操作更加容易;
- 避免多层异步调用嵌套问题(回调地狱);
四. promise的属性
Promise对象上主要有着PromiseState
和PromiseResult
两个属性。
(1)PromiseState:
PromiseState 表示Promise对象中函数的执行结果,主要有以下三个状态:
- pending: 一个等待状态,或者进行中状态,表示还没有得到结果;
fulfill(resolve):一个满足状态,当主动回调resolve时,就处于该状态,并且会回调.then();
reject:一个拒绝状态,当主动回调reject时,就处于该状态,并且会回调.catch();
注:
- 这三种状态不受外界影响,而且状态只能从pending改变为resolved或者rejected,并且不可逆。
- 一个 promise 对象只能改变一次状态, 无论变为成功还是失败, 都会有一个结果数据, 成功的结果数据一般称为 value, 失败的结果数据一般称为 reason。
(2)PromiseResult
PromiseResult 表示Promise对象函数中传递给resolve
或者reject
方法的参数,一般会在then
方法中通过value或者reason形参来接收到这个参数。
五.Promise对象的API
(1)catch
可以在then中指定失败的回调函数,还可以通过Promise对象.then().catch()
来进行失败函数的执行:
<script>
const p = new Promise((resolve,reject)=>{
reject('aaa');
})
p.then(value=>{
console.log(value);
}).catch(reason=>{
console.error(reason);
})
</script>
注:如果在then和catch中都指定了失败的回调函数,那么只会执行then方法中定义的回调函数。
(2)Promise.resolve()
Promise中提供了一个resolve方法来快速创建Promise对象,resolve方法中可以接受参数,如果传入的参数为非Promise类型的对象, 则返回的结果为成功promise对象。如果传入的参数为 Promise 对象, 则参数的结果决定了resolve 的结果。
<script>
const p1 = Promise.resolve('aaa');
const p2 = Promise.resolve(new Promise((resolve,reject)=>{
reject();
}));
console.log(p1);
console.log(p2);
</script>
(3)Promise.reject()
reject的方法和Promise.resolve()
方法很相似,不同的地方在于无论reject方法中传递的是什么参数,PromiseState的状态都是rejected。
(4)Promise.all()
all方法中需要传入包含n个promise的数组,结果返回一个新的 promise, 只有所有的promise都成功才会成功, 但是有一个失败了就直接失败。
<script>
const p1 = Promise.resolve('aaa');
const p2 = Promise.resolve('bbb');
const p3 = Promise.resolve('ccc');
const result1 = Promise.all([p1,p2,p3]);
const p4 = Promise.resolve('aaa');
// 注意,x下面是reject
const p5 = Promise.reject('bbb');
const p6 = Promise.reject('ccc');
const result2 = Promise.all([p4,p5,p6]);
console.log(result1);
console.log(result2);
</script>
注:如果当遇到第一个执行失败的promise时,接下来的promise就不会再继续调用了。
(5)Promise.race()
传入多个Promise对象,第一个完成的 Promise 的结果状态就会是最终的结果状态。
<script>
const p1 = Promise.resolve(new Promise((resolve,reject)=>{
setTimeout(()=>{
resolve('aaa');
},1000);
}));
const p2 = Promise.resolve('bbb');
const p3 = Promise.resolve('ccc');
const result = Promise.race([p1,p2,p3]);
console.log(result);
</script>
六.promise对象的注意事项
(1)怎样改变 promise 的状态?
- resolve(value): 如果当前是 pending 就会变为 resolved;
- reject(reason): 如果当前是 pending 就会变为 rejected;
- 抛出异常: 如果当前是 pending 就会变为 rejected;
(2)promise 指定多个成功/失败回调函数的调用顺序
改变 promise 状态和指定回调函数谁先谁后;
(3) promise 状态和指定的改变,回调函数的执行顺序
注:不是指promise中的resolve/reject
函数和then中调用的具体函数的执行顺序,而是单指promise对象更改状态和确定回调函数(并没有执行)是哪个函数发生的先后顺序。
1> 正常情况下是先指定回调再改变状态, 但也可以先改状态再指定回调;
2> 如何先改状态再指定回调?
- 在执行器中直接调用 resolve()/reject();
- 延迟更长时间才调用 then();
3>何时才能得到数据?
- 如果先指定的回调, 那当状态发生改变时, 回调函数就会调用, 得到数据;
- 如果先改变的状态, 那当指定回调时, 回调函数就会调用, 得到数据;
(4)promise.then()返回新的 promise 的结果状态如何决定?
- 如果抛出异常, 新 promise 变为 rejected, reason 为抛出的异常;
- 如果返回的是非 promise 的任意值, 新 promise 变为 resolved, value 为返回的值;
- 如果返回的是另一个新 promise, 此 promise 的结果就会成为新 promise 的结果;
(5)promise 串连多个操作任务
如果promise 的then()方法中会返回一个新的 promise对象, 可以使用 then()的链式调用来处理多个不同模块下的Promise对象。
<script>
function test() {
const p = Promise.resolve('aaa');
p.then(value => {
console.log(value);
return new Promise((resolve, reject) => {
resolve('bbb');
})
}).then(value => {
console.log(value);
})
}
test();
</script>
注:可以使用promise这个特性来分别处理异步请求,解决回调地狱的问题。
(6)promise 的异常传透
当使用 promise 的 then 链式调用时, 可以在最后指定失败的回调,如果前面任何操作出了异常, 都会传到最后失败的回调中处理。如果对异常的处理方式相同,不再需要每次都在then函数中写相同的代码,只需要在最后进行统一的catch
捕获就行。
<script>
const p = new Promise((resolve,reject)=>{
resolve();
});
p.then(value=>{
console.log('aaa');
}).then(value=>{
console.log('bbb');
throw 'aaa';
}).then(value=>{
console.log('ccc')
}).catch(err=>{
console.error(err);
})
</script>
(7) 中断promise调用链
当使用 promise 的 then 链式调用时, 如果想要中断promise的调用链,可以在回调函数中返回一个 pending 状态的 promise 对象,不再调用后面的回调函数。
<script>
const p = new Promise((resolve,reject)=>{
resolve();
})
p.then(value=>{
console.log('aaa');
}).then(value=>{
console.log('bbb');
// 单纯的返回Promise对象,并不能阻止后面then的运行
return new Promise(()=>{});
}).then(value=>{
console.log('ccc');
})
</script>
七.使用async和await关键字
(1)async关键字
async
函数的返回值为 promise 对象,promise 对象的结果由 async 函数执行的返回值决定。
- 当返回非Promise的数据类型,PromiseState作为成功来处理;
- 当返回Promise数据类型,PromiseState根据Promise返回结果决定;
- 当抛出异常时,PromiseState作为失败来处理;
(2)await关键字
await 右侧的表达式一般为 promise 对象, 但也可以是其它的值, 如果表达式是 promise 对象, await 返回的是 promise 成功的值; 如果表达式是其它值, 直接将此值作为 await 的返回值。
注:
- await 必须写在 async 函数中, 但 async 函数中可以没有 await;
- 如果 await 的 promise 失败了, 就会抛出异常, 需要通过 try...catch 捕获处理;