了解
ES7 提出的async 函数,让 JavaScript 对于异步操作有了终极解决方案;async函数实际上是Generator函数的语法糖,使用关键字async来表示,在函数内部使用await来表示异步,相较于Generator,有以下改进:
- 内置执行器。Generator 函数的执行必须依靠执行器,而 Aysnc 函数自带执行器,调用方式跟普通函数的调用一样
- 更好的语义。async 和 await 相较于 * 和 yield 更加语义化
- 更广的适用性。co 模块约定,yield 命令后面只能是 Thunk 函数或 Promise对象。而 async 函数的 await 命令后面则可以是 Promise 或者 原始类型的值(Number,string,boolean,但这时等同于同步操作)
- 返回值是 Promise。async 函数返回值是 Promise 对象,比 Generator 函数返回的 Iterator 对象方便,可以直接使用 then() 方法进行调用
为什么使用
一个函数如下:
function fetchUser() {
return new Promise((resolve, reject) => {
axios('http://xxx/XXX')
.then((data) => {
resolve(data.json());
}, (error) => {
reject(error);
})
});
}
使用promise调用如下:需要使用then函数,如果有多重调用,虽然不会出现回调地狱,但是出现多层嵌套的情况,语义化不明显,代码流出也不能很好的表示执行。。
/**
* Promise 方式
*/
function getUserByPromise() {
fetchUser()
.then((data) => {
console.log(data);
}, (error) => {
console.log(error);
})
}
getUserByPromise();
使用async方式,能够完美解决多层嵌套问题,并且语义明显、流程清晰
/**
* async 方式
*/
async function getUserByAsync(){
let user = await fetchUser();
return user;
}
getUserByAsync()
.then(v => console.log(v));
实际使用
初步使用
- 每一个async函数在执行之后,都会自动返回一个Promise对象:
- 如果我们手动在async函数中使用return关键字返回一个直接量,async就会把这个直接量通过Promise.resolve()封装成Promise对象
- 如果async函数没有返回值,则它返回Promise.resolve(undefined)
async test() {
// 什么都不写
}
async test1() {
let rel = await Promise.resolve('success')
return rel
}
let result = test()
let result1 = test1()
console.log(result) // 打印出Promise对象
console.log(result1) // 打印出Promise对象
- await必须在async函数里面使用,否则会报错
- await后面需要跟Promise对象,不然就没有意义,而且await后面的Promise对象不必写then,因为await的作用之一就是获取后面Promise对象成功状态传递出来的参数,如下图:
错误处理
await可以直接获取到后面Promise成功状态传递的参数,但是却捕捉不到失败状态;如果第一个await发生错误,后面的await将形成阻塞。所以,我们可以通过两种方式来进行错误捕捉
1、因为async返回的原本就是一个Promise对象,所以我们可以直接在调用的时候跟一个catch回调即可
// 紧跟上面例子
test1().then(response => {
console.log(response)
}).catch(error => {
// 捕捉异常
console.log(error)
})
2、在await外面包裹一层try…catch,进行错误的捕捉
try {
let result = await performanceApi.getPerformanceManageList(params)
let {data} = result
} catch (error) {
// 异常捕捉
console.log(error)
}
3、可能有用,可以了解:使函数同时返回两个值,如下:
async function test () {
try {
let res = await asyncFun()
return [null, res]
} catch (e) {
return [e, null]
}
}
async function func() {
let [err, res] = await errorCaptured(asyncFunc)
if (err) {
//... 错误捕获
}
//...
}
Async/await的陷阱
虽然await让你的代码看起来想同步的,但是一定要记住,这些方法其实还是在以异步的方式执行,以下代码使没有必要的:
async test(authorId) {
// 这样写就意味着:fun1执行完,才会执行fun2,而fun1和fun2是没有依赖关系的,这样执行会花费多余的时间,不合理!
const rel1 = await fun1()
const rel2 = await fun2()
return {
rel1,
rel2
}
}
应该修改为,异步执行,没有顺序
async test(authorId) {
// 提前执行
const relPromise = fun1()
const relsPromise = fun2()
const rel1 = await relPromise
const rel2 = await relsPromise
return {
rel1,
rel2
}
}