1. async
async后面承接的函数有不同的返回值,但无论返回什么,async都会将该返回值封装成一个Promise对象
(1)若该函数本身返回一个Promise对象,async直接接收这个Promise对象
// getData函数本身返回一个Promise对象
const getData = data => {
return Http.get(getDataUrl, data)
.then(res => {
return Promise.resolve(res)
})
.catch(error => {
return Promise.reject(error)
})
}
// async关键字承接getData函数返回的Promise对象
async getData() // 打印输出res值
```
(2)若该函数本身返回一个直接量,async通过Promise.resolve()把直接量封装成Promise对象
async function getData() {
return "hello async";
}
getData(); // Promise { 'hello async' }
// "hello async"字符串被async使用Promise.resolve()封装成Promise对象的形式。
(3)若该函数没有返回值,则async会封装返回值为Promise.resolve(undefined)
2. await
首先明确,await等待的是一个表达式(见await)
await除了等待async异步函数外,也可以等待其他任意值
也就是说,await等待的表达式其实并没有特别要求,不仅仅用于等 Promise 对象,也可以等任意表达式的结果,你用await 承接普通函数调用或承接任意一个直接量也是完全可以的。
function getData() {
return "同步"
}
async function getData2() {
return Promise.resolve("hello 异步")
}
const v1 = await getData() // await等待同步
const v2 = await getData2() // await等待异步
console.log(v1, v2)
(1)如果await等到的不是一个 Promise 对象,那 await 表达式的运算结果就是它等到的东西。
(2)如果await等到的是一个 Promise 对象,await 会做如下行为:首先阻塞后面要执行的代码,等着这个 Promise 对象 resolve,得到 resolve 的值,作为 await 表达式的运算结果,再继续执行后面的代码。
3. async和await的优势
async/await 的优势体现在如果Promise的then链很长的时候, async/await 的写法会大大简化复杂的then链。
假设一个业务,分多个步骤完成,每个步骤都是异步的,而且依赖于上一个步骤的结果。
(1)首先用 setTimeout
来模拟异步操作:
/**
* 传入参数 n,表示这个函数执行的时间(毫秒)
* 执行的结果是 n + 200,这个值将用于下一步骤
*/
function takeLongTime(n) {
return new Promise(resolve => {
setTimeout(() => resolve(n + 200), n);
});
}
function step1(n) {
console.log(`step1 with ${n}`);
return takeLongTime(n);
}
function step2(n) {
console.log(`step2 with ${n}`);
return takeLongTime(n);
}
function step3(n) {
console.log(`step3 with ${n}`);
return takeLongTime(n);
}
(2)进一步,用 Promise 方式来实现这三个步骤的处理:
function doIt() {
console.time("doIt");
const time1 = 300;
step1(time1)
.then(time2 => step2(time2))
.then(time3 => step3(time3))
.then(result => {
console.log(`result is ${result}`);
console.timeEnd("doIt");
});
}
doIt();
// c:\var\test>node --harmony_async_await .
// step1 with 300
// step2 with 500
// step3 with 700
// result is 900
// doIt: 1507.251ms
(3)最后,用 async/await 来实现:
async function doIt() {
console.time("doIt");
const time1 = 300;
const time2 = await step1(time1);
const time3 = await step2(time2);
const result = await step3(time3);
console.log(`result is ${result}`);
console.timeEnd("doIt");
}
doIt();
4. async、await在Vue钩子函数中使用
在构建项目时,我们通常需要在created或mounted函数中,向后端发送请求获取数据,利用获取到的数据做进一步处理。
当必须要等到请求返回数据才能做进一步判断处理时,就需要使用到async和await,如下:
async created() {
// 通过getUserData拿到userType等信息
await this.getUserData()
//拿到userType 判断用户角色,从而展示不同信息
if(this.userType==1){
this.isAgent=true
}
}
在vue的钩子函数中使用async、await,有几点需要注意:
(1)钩子函数里,await加在定时器前面或加在其他类似的需要延迟执行的函数前面不会起效果。
async created() {
await setTimeout(() => {
console.log(1);
}, 100);
console.log(2);
},
async mounted() {
await setTimeout(() => {
console.log(3);
}, 100);
console.log(4);
}
// 输出结果: 2 4 1 3
// 结论: 所以await加给setTimeout是没有生效的,定时器并没有影响生命周期执行
分析:
js 是单线程,解析代码逐行解析,遇到异步任务放到webAPI去执行。
首先解析created钩子,把created的settimeout任务放进去;
解析到mounted钩子,也把settimeout放到webAPI去执行;
但此时发现webAPI里面已经有个任务队列(created放进去的),因此需要排队;
如果mounted的settimeout的延时为0, created的settimeout 的延时为100, 则这个webAPI里面mounted的settimeout可以插队执行。
(2)钩子函数里使用async,await,后面只能跟promise,否则加async,awiat没意义。
// await 后面加同步代码可以,但是没有意义
// async,awiat是专门为了解决promise回调函数多层嵌套的问题
async created() {
await this.userData(); // 这个函数负责发请求获取数据,axios就是使用了promise
//拿到userType 判断用户不是代理人展示不同信息
if(this.userType==1){
this.isAgent=true;
}
}
// 结论: 这样写该周期函数下面的代码是会等待请求回来再执行的这样写是没问题的! 但仅仅限该周期函数!
(3)钩子函数 created、mounted里若加了async,await,并想要同步执行这些代码,需要将这些代码放在同一个钩子函数里面,否则并不会如预期般先执行完created再执行mounted。
data() {
return {
name: ''
}
},
async created() {
// this.userInfo() 获取数据赋值name
await this.userInfo()
...
},
mounted() {
console.log(this.name) // 打印为''
}
// 结论: 不要以为mounted就一定比created后执行, 因为created加了async,await
// mounted函数不等created伺候完promise就先执行了
// 所以你在mounted里面不一定能拿到this.name进行赋值!
// 像这种有依赖上一个代码结果的最好写在一起
// 如下:
async created() {
// this.userInfo() 发送请求获取数据,然后赋值给this.name ---- this.name = res.data.name
await this.userInfo() // 拿到数据赋值成功
console.log(this.name) // 打印成功
},