一、Promise与异步的关系
之前做练习遇到Promise与SetTimeout的混合使用,自己发现对于Promise的理解不够彻底,所以今天就来看看这方面的问题。
首先Promise的主要作用是能够处理异步问题,那么什么是异步呢?与它相反的同步又是怎么理解的呢?
1.1 同步与异步概念
1.1.1 同步
单线程按顺序执行,执行步骤有明确的先后顺序。
例如开车,需要先进车门,发动引擎再驾驶。这三部就是有明确的先后顺序,一步步执行下来。
js碰到的一般场景是单线程顺序执行的。
1.1.2 异步
该步骤不进入主线程,而是把需要执行的步骤先挂在队列中。等主线程有执行空间,再进行执行操作。
例如上述例子,某人在进车门时收到短信需要回复,他把回复操作挂在进车门,发动引擎之后才操作。
js要是使用到setTimeout,请求接口ajax相关的操作会碰到异步操作。
1.2 Promise为什么能解决异步问题?
要是遇到多个异步进行嵌套,会产生回调地狱(回调地狱会造成维护性能差,多次重复操作可读性差)
而ES6中的Promise被列为正式规范就是为了解这个问题,并且可以制定多种异步的执行规则或多种状态判断执行结果。
二、Promise原理
2.1 Promise是什么对象?
我们先使用console.dir(promise)可以打印出该对象的所有属性以及相关属性值。
可以看出promise是一个构造函数,所以每次使用都需要new新建实例。
观察打印结果,看到它本身含有reject、resolve方法。在原型上又有then、catch方法。
2.2 promise的简单使用
从上述代码中可以看出来,Promise构造函数中有两个参数(resolve, reject)。这两个参数与Promise的三种状态有关。
Promise的三种状态:
- pending(进行中)
- fulfilled(执行成功)
- rejected(执行失败)。
在执行Promise的时候:即进入队列中,它的状态会是pending,会根据执行结果返回fulfilled或者rejected。(注意:当次Promise三种状态只能存在一种,最终结果不是fulfilled就是rejected,而且Promise状态不可回滚)
在count()后面会看到执行了它的两种方法:
- .then()方法,这就是链式调用该Promise(即op)中的执行成功resolve()中的参数。反正要是执行失败就是取reject()中的参数。
- .catch()方法,这个作用就是当在执行上述.then()中报错时会抛出异常(控制台不会看到报错信息),就是会执行.catch()方法里面的代码。
2.3 使用Promise来实现链式调用
<script>
export default {
data(){
return{
}
},
mounted(){
// 2.3 使用Promise来实现链式调用
this.promise1().then((outPrint) => {
console.log(outPrint)
this.promise2().then(
//第一,resolve参数
(outPrint2) => {
console.log(outPrint2)
return this.promise3()
},
//第二,reject参数
(outPrint2) => {
console.log(outPrint2)
return this.promise3()
})
})
},
methods:{
// 2.3 使用Promise来实现链式调用
promise1(){
var op1 = new Promise( (resolve,reject) => {
//异步操作
setTimeout(()=>{
console.log('我是promise1内部的console')
resolve('我从promise1里面出来了')
},2000)
})
return op1
},
promise2(){
var op2 = new Promise( (resolve,reject) => {
//异步操作
setTimeout(()=>{
console.log('我是promise2内部的console')
reject('我从promise2里出来reject')
},2000)
})
return op2
},
promise3(){
var op3 = new Promise( (resolve,reject) => {
//异步操作
setTimeout(()=>{
console.log('我是promise3内部的console')
// resolve('我从promise3里面出来了')
},2000)
})
return op3
}
}
}
</script>
运行截图:
由此可以看出该promise比相对的回调函数嵌套可读性已经复杂度减少了不少,即简单又灵活。
2.4 使用Promise.all,Promise.race
实例化好每个promise之后,可以使用Promise.all([实例1,实例2,实例3]),以数组的形式进行传入,这样可以并行执行多个异步操作。最终.then()中返回的参数res,也是以数组的形式进行把resolve()中的参数返回出来。Promise.race([])也是如此使用。
<script>
export default {
data(){
return{
}
},
mounted(){
this.comA()
this.comR()
},
methods:{
comA(){
console.time('timeRangeA')
Promise.all([this.promise1(), this.promise2(),this.promise3()])
.then( (res) => {
console.log(res)
console.timeEnd('timeRangeA')
} )
},
comR(){
console.time('timeRangeR')
Promise.race([this.promise1(), this.promise2(),this.promise3()])
.then( (res) => {
console.log(res)
console.timeEnd('timeRangeR')
} )
},
promise1(){
var op1 = new Promise( (resolve,reject) => {
//异步操作
setTimeout(()=>{
resolve('我从promise1里面出来了')
},10000)
})
return op1
},
promise2(){
var op2 = new Promise( (resolve,reject) => {
//异步操作
setTimeout(()=>{
resolve('我从promise2里面出来了')
},1000)
})
return op2
},
promise3(){
var op3 = new Promise( (resolve,reject) => {
//异步操作
setTimeout(()=>{
resolve('我从promise3里面出来了')
},2000)
})
return op3
}
}
}
</script>
运行截图:
2.4.1 all与race的区别
- all是以谁最慢的时间进行输出,如上述代码promise1()是延迟10秒输出,三者最慢。最后执行.then()的时间也是10秒左右,promise2()与promise3()等待promise1()执行完再输出。
- race是以谁最快的时间进行输出,如上述代码promise2()是延迟1秒输出,而promise2()执行完直接不等待promise1()与promise3(),直接执行.then()。
2.4.2 all与race的使用场景
- all:渲染一个页面,需要把所有的图片等资源全部获取完再显示页面。
- race:接口请求获取一个资源,要是请求超时,直接不再请求此资源并弹出请求超时。