在开发过程中,时有遇到多个请求时,后一个请求需要依赖前一个请求拿到的结果进行请求。为处理这种情况,下面对 ES6 中Promise方法和asyncawait关键字进行总结

一、Promise 方法使用

在编写 vue 组件时,我们的请求大多都写在 methods 对象中,要想将请求封装成独立的方法,通常会使用如下形式:

1
2
3
4
5
6
7
8
9
10
11
testGet() {
this.$axios('https://jsonplaceholder.typicode.com/comments/1')
.then((res) => {
if (res.status === 200) {
console.log('请求成功', res)
}
})
.catch((err) => {
console.log('请求失败', err)
})
},

这样在其他方法内需要调用该接口则直接调用该 testGet 方法即可。这个时候如果需要当前请求的结果作为另一个请求的参数,那么显然这个方法就不能这样写,因为这样就无法同步的拿到当前请求的结果

于是可以使用Promise将以上方法改写,Promise对象是一个构造函数,用来生成Promise实例。将上面的请求方法用 Promise 构造函数改造后如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18

testGet() {
const that = this
return new Promise((resolve, reject) => {
that
.$axios('https://jsonplaceholder.typicode.com/comments/1')
.then((res) => {
if (res.status === 200) {
console.log('请求成功', res)
resolve(res)
}
})
.catch((err) => {
console.log('请求失败', err)
reject(err)
})
})
},

这样,在其他方法中,想要获取该接口的返回值来作为参数继续请求时就可以使用如下代码

1
2
3
4
5
6
7
8
9
10
11
12

useTestGet() {
this.testGet().then((res) => {
let queryData = res.data
this.$axios({
url: 'http://jsonplaceholder.typicode.com/posts',
data: queryData,
}).then((res) => {
console.log(res)
})
})
},

当然这样写功能是可以实现的,但是可读性较差,而且当请求数过多时,且都需要一个先后顺序,这就会形成传说中的回调地狱,回调地狱大概就长如下的样子吧:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
testPromise() {
this.$axios('https://jsonplaceholder.typicode.com/comments/1')
.then((res) => {
console.log('comment1拿到了', res)
})
.then(() => {
this.$axios('https://jsonplaceholder.typicode.com/comments/2')
.then((res) => {
console.log('comment2拿到了', res)
})
.then(() => {
this.$axios('https://jsonplaceholder.typicode.com/comments/3')
.then((res) => {
console.log('comment3拿到了', res)
})
.then(() => {
this.$axios('https://jsonplaceholder.typicode.com/comments/4').then((res) => {
console.log('comment4拿到了', res)
})
})
})
})
},

要解决回调地狱,也为了使代码更具可读性,ES2017 推出asyncawait关键字

二、async 和 await

async字面意思就是“异步”,该关键字用在一个函数的定义前面,用于表示这个函数是异步操作,这个函数的返回结果将是一个Promise对象。如下:

1
2
3
4
5
async function testAsync() {
return 'hello async'
}
const result = testAsync()
console.log(result) // 控制台输出 Promise {<fulfilled>: "hello async"}

await字面意思等待,等待的就是异步函数的执行结果,这个时候会阻塞后面的代码,等待着 Promise 对象的 resolve,然后得到 resolve 的值,作为 await 表达式的运算结果。也就是说,代码执行到await时,在没有拿到结果之前,是不会继续执行下面的代码的。看下面的例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
function getData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
console.log('1s后执行的代码')
resolve(1)
}, 1000)
})
}
async function testAsync() {
const data = await getData()
console.log('await之后的代码...')
console.log('await的结果', data)
}
testAsync()
// 控制台输出如下
// 1s后执行的代码
// await之后的代码...
// await的结果 1

这里注意await关键字一定是在async定义的函数内部使用的,出了async定义的函数,使用await是无效的,也是不被允许的。

通过以上代码的输出可以看出,testAsync 方法只有在await 之后的 Promise 执行完 resolve 之后,才会继续向下执行那两行打印语句。

为了解决上面说到的回调地狱,我们使用async定义异步函数,结合await等待异步执行结果,然后同步调用异步函数,于是改写如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
async testAsync() {
const that = this
function getComment1() {
return new Promise((resolve, reject) => {
that.$axios('https://jsonplaceholder.typicode.com/comments/1').then((res) => {
resolve(res)
})
})
}
function getComment2() {
return new Promise((resolve, reject) => {
that.$axios('https://jsonplaceholder.typicode.com/comments/2').then((res) => {
resolve(res)
})
})
}
function getComment3() {
return new Promise((resolve, reject) => {
that.$axios('https://jsonplaceholder.typicode.com/comments/3').then((res) => {
resolve(res)
})
})
}
function getComment4() {
return new Promise((resolve, reject) => {
that.$axios('https://jsonplaceholder.typicode.com/comments/4').then((res) => {
resolve(res)
})
})
}
const comment1 = await getComment1()
console.log('comment1拿到了', comment1)
const comment2 = await getComment2()
console.log('comment2拿到了', comment2)
const comment3 = await getComment3()
console.log('comment3拿到了', comment3)
const comment4 = await getComment4()
console.log('comment4拿到了', comment4)
},

至此,就解决了回调地狱的问题,当有多个请求时,也可以使用 async 和 await 写出高可读性的代码了。

参考文章:

https://segmentfault.com/a/1190000007535316

https://www.jianshu.com/p/b4fd76c61dc9

https://es6.ruanyifeng.com/#docs/promise