什么是async/await?
在ES2017中,async和await成为两个新关键字。async用来修饰一个函数,说明该函数是一个异步函数。而在异步函数中,我们可以使用await来等待一个Promise对象,然后获得该对象的值。
在Vue中,我们通常会使用一些基于Promise的异步操作,比如调用接口获取数据,或者异步加载图片等等。使用async/await可以让我们更加清晰地处理这些异步操作。
如何使用async/await?
使用async/await的基本语法非常简单。我们只需要将一个函数声明为async函数,然后在需要等待异步操作的地方使用await来等待Promise对象的返回值即可。
以获取数据为例,我们可以定义一个异步函数getArticleById,然后在函数体内等待http请求的返回值:
async function getArticleById(id) {
const response = await fetch(`/api/articles/${id}`);
return response.json();
}
在Vue中,我们通常会使用axios来调用接口获取数据。我们可以将axios封装成一个异步函数,然后在Vue组件中使用async/await来获取数据。
以获取博客列表为例,我们可以定义一个异步函数getBlogList:
async function getBlogList() {
const response = await axios.get('/api/blogs');
return response.data;
}
然后,在Vue组件中,我们可以使用async/await来获取数据,并将数据绑定到模板中:
<template>
<div>
<div v-for="blog in blogs" :key="blog.id">{{blog.title}}</div>
</div>
</template>
<script>
async function getBlogList() {
const response = await axios.get('/api/blogs');
return response.data;
}
export default {
data() {
return {
blogs: []
}
},
async mounted() {
this.blogs = await getBlogList();
}
}
</script>
使用async/await处理多个异步操作
在实际开发中,我们通常会遇到多个异步操作需要同时处理的情况。比如,在Vue组件中,我们需要从不同的接口获取数据,然后再进行处理或渲染。这时候,我们可以使用Promise.all()方法来一次性等待所有异步操作完成。
以获取文章和评论为例,我们可以定义两个异步函数getArticle和getComments:
async function getArticle(id) {
const response = await axios.get(`/api/articles/${id}`);
return response.data;
}
async function getComments(articleId) {
const response = await axios.get(`/api/articles/${articleId}/comments`);
return response.data;
}
然后,我们可以将这两个异步操作封装到一个async函数中,使用Promise.all()等待两个操作同时完成:
async function getArticleWithComments(articleId) {
const [ article, comments ] = await Promise.all([
getArticle(articleId),
getComments(articleId)
]);
return {
article,
comments
};
}
在Vue组件中,我们可以使用async/await来获取所有数据,并将数据绑定到模板中:
<template>
<div>
<h1>{{article.title}}</h1>
<p>{{article.content}}</p>
<ul>
<li v-for="comment in comments" :key="comment.id">{{comment.content}}</li>
</ul>
</div>
</template>
<script>
async function getArticle(id) {
const response = await axios.get(`/api/articles/${id}`);
return response.data;
}
async function getComments(articleId) {
const response = await axios.get(`/api/articles/${articleId}/comments`);
return response.data;
}
async function getArticleWithComments(articleId) {
const [ article, comments ] = await Promise.all([
getArticle(articleId),
getComments(articleId)
]);
return {
article,
comments
};
}
export default {
data() {
return {
article: {},
comments: []
}
},
async mounted() {
const data = await getArticleWithComments(this.$route.params.articleId);
this.article = data.article;
this.comments = data.comments;
}
}
</script>
使用try/catch处理异常
在使用async函数时,我们也需要注意异常处理。当异步函数中发生错误时,我们可以使用try/catch语句来捕捉异常,并进行相应的处理。
以获取用户信息为例,我们可以定义一个异步函数getUserInfo。如果用户未登录,则从服务器获取用户信息时会返回未授权的错误。我们可以使用try/catch语句捕捉该错误,并进行相应的处理:
async function getUserInfo() {
try {
const response = await axios.get('/api/user');
return response.data;
} catch (error) {
if (error.response && error.response.status === 401) {
// 用户未登录
return null;
} else {
// 其它异常
throw error;
}
}
}
async 关键字差不多了,我们再来考虑await 关键字,await是等待的意思,那么它等待什么呢,它后面跟着什么呢?其实它后面可以放任何表达式,不过我们更多的是放一个返回promise 对象的表达式。注意await 关键字只能放到async 函数里面
现在写一个函数,让它返回promise 对象,该函数的作用是2s 之后让数值乘以2
function doubleAfter2seconds(num) {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve(2 * num)
}, 2000);
} )
}
现在再写一个async 函数,从而可以使用await 关键字, await 后面放置的就是返回promise对象的一个表达式,所以它后面可以写上 doubleAfter2seconds 函数的调用
async function testResult() {
let result = await doubleAfter2seconds(30);
console.log(result);
}
testResult();
打开控制台,2s 之后,输出了60.
现在我们看看代码的执行过程,调用testResult 函数,它里面遇到了await, await 表示等一下,代码就暂停到这里,不再向下执行了,它等什么呢?等后面的promise对象执行完毕,然后拿到promise resolve 的值并进行返回,返回值拿到之后,它继续向下执行。具体到 我们的代码, 遇到await 之后,代码就暂停执行了, 等待doubleAfter2seconds(30) 执行完毕doubleAfter2seconds(30) 返回的promise 开始执行,2秒 之后,promise resolve 了, 并返回了值为60, 这时await 才拿到返回值60, 然后赋值给result, 暂停结束,代码才开始继续执行,执行 console.log语句。
就这一个函数,我们可能看不出async/await 的作用,如果我们要计算3个数的值,然后把得到的值进行输出呢?
async function testResult() {
let first = await doubleAfter2seconds(30);
let second = await doubleAfter2seconds(50);
let third = await doubleAfter2seconds(30);
console.log(first + second + third);
}
6秒后,控制台输出220, 我们可以看到,写异步代码就像写同步代码一样了,再也没有回调地域了。
注意:
async 和 await 基于 promise 的。 使用 async 的函数将会始终返回一个 promise 对象。 这一点很重要,要记住,这可能是你遇到的容易犯错的地。
在使用 await 的时候我们暂停了函数,而非整段代码。
async 和 await 是非阻塞的。
你仍然可以使用 Promise 例如 Promise.all()。