async/await
是ES7中被提实现异步操作的技术,相对比较新,好在babel爸爸已对它支持,我们可以欢快的对它进行使用。了解async/await
前要求对Promise有一定的理解,或可阅读 深入理解 Promise 一文。盗图
# async - 定义异步函数
async
译:异步,是 Generator 函数的语法糖。该函数会返回一个promise对象,可以使用then
方法添加回调函数,如果在函数内直接 return
,Async会通过Promise.resolve()将其封装成Promise()对象,也可以通过.then
添加回调函数
async function pms() {
return 'abc'
}
console.log(pms()) // [object Promise] { ... }
以上方法执行返回了一个promise对象,其执行等效于
async function pms() {
return new Promise((resolve, reject) => {
resolve('abc')
})
}
在没有配合await
时,我们可以调用promise的.then
方法得到执行结果。当然也会有.catch
方法
pms().then(res => {
console.log(res) // 'abc'
}).catch(e => {
console.log('错误')
})
# await - 暂停异步函数的执行
await
,即:async wait,旨在等待异步执行结束,使用于async函数内部。异步未执行结束,阻塞当前代码。嗯哼?阻塞!!!
与线程阻塞不同的是,await
的阻塞发生在 async
函数内部,可以理解为一个异步的阻塞。跟在await
后的JS表达式,可以等待很多类型的事件,但初衷是用于等待Promise对象。如果await
的对象是promise对象,则阻塞异步函数代码执行等待promise的resolve
状态,如果是同步执行的表达式则立即执行。
# async/await的使用
function asyncFunc(time) {
return new Promise((resolve, reject) => {
setTimeout(() => { // 用setTimeout模拟异步
resolve('async result')
}, time)
})
}
function normalFunc() {
console.log('normal result')
}
async function awaitDemo() {
await normalFunc() // 执行立即打印 normal result
const res = await asyncFunc(1000)
console.log(res) // 执行1s后打印 ‘async result’
}
awaitDemo() // 执行
以上的例子比较常规,再看看下面这个例子
async function func() {
console.log('123')
}
async function run() {
const res = await func()
console.log(res) // 123
}
run()
该例子中,func
看似一个普通函数,但经async
定义后,会返回一个promise对象,此时的await
等待的就是该promise对象的resolve
参数,即123
。
对比上一个例子中的normalFunc
,主要有两个理解点
(1)增加了async的普通函数变成了一个异步函数,await等待的对象为promise对象,返回resolve参数
(2)await可以等待任何东西,如果等待的是普通内容,则直接返回该内容
# 【填坑1】异常处理
细心的你应该已经发现,await
返回的是promise的resolve
参数,但对于catch
却没有实际的处理。那当我们的请求发生异常时,该怎么办?答案是使用try-catch
,在Promise中的.catch()
分支会流入catch
中
function asyncFunc(time) {
return new Promise((resolve, reject) => {
setTimeout(() => { // 用setTimeout模拟异步
reject('promise reject')
}, time)
})
}
// 为了处理Promise.reject 的情况我们应该将代码块用 try catch 捕获一下
async function awaitDemo() {
try {
const res = await asyncFunc(1000)
console.log(res)
} catch (err) {
console.log(err.message || 'Uncatch Error')
}
}
awaitDemo() // 如果没有使用try-catch,执行会报错
# 【填坑2】并行请求
对于一个真实的业务需求,通常会有多个异步请求需要同时执行,如获取左侧目录树,和登录账户的用户信息等。因await遇到异步会阻塞,当一个async函数内有多个异步函数需要调用时,就会出现相互等待的现象。天哪,“异步” 变成了 “同步”了,这不是我们所希望的
function asyncFunc() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('request done')
})
})
}
async function bugDemo() {
await asyncFunc()
await asyncFunc()
await asyncFunc()
console.log('finish')
}
以上代码正常执行了,没有报出异常,但仔细观察控制台 timeline 可以发现,三个函数是同步顺序执行的,其罪魁祸首就是await
的等待机制。那怎么解决这个问题?
其实async-await
只是promise的语法糖,但其并不能代替Promise,从对异常的处理上就可以看出,还需要引入try-catch
,本问题是另一个体现。解决办法需要使用promise的.all()
promise.all( iterable )
,当所有请求都为resolve时,返回resolve
状态
async function correctDemo() {
const f1 = asyncFunc()
const f2 = asyncFunc()
const f3 = asyncFunc()
await Promise.all([f1, f2, f3]);
console.log('finish')
}
# 【填坑3】await必须在async的上下文中
await
并不只是使用在async
函数中即可,还必须在asyn
函数的上下文中
// 虽在async函数里使用,但在forEach上下文中,异常
async function errorDemo() {
[1, 2, 3, 4, 5].forEach(item => {
await item;
});
}
errorDemo() // SyntaxError: await is only valid in async function
// await 必须在async函数的上下文中
async function correctDemo() {
let arr = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i ++) {
await arr[i];
}
}