? async/await 是ES7中被提實(shí)現(xiàn)異步操作的技術(shù),相對比較新,好在babel爸爸已對它支持,我們可以歡快的對它進(jìn)行使用。了解async/await前要求對Promise有一定的理解,或可閱讀 深入理解 Promise 一文。盜圖

# async - 定義異步函數(shù)
? async 譯:異步,是 Generator 函數(shù)的語法糖。該函數(shù)會返回一個(gè)promise對象,可以使用then方法添加回調(diào)函數(shù),如果在函數(shù)內(nèi)直接 return,Async會通過Promise.resolve()將其封裝成Promise()對象,也可以通過.then添加回調(diào)函數(shù)
async function pms() {
return 'abc'
}
console.log(pms()) // [object Promise] { ... }
? 以上方法執(zhí)行返回了一個(gè)promise對象,其執(zhí)行等效于
async function pms() {
return new Promise((resolve, reject) => {
resolve('abc')
})
}
? 在沒有配合await時(shí),我們可以調(diào)用promise的.then方法得到執(zhí)行結(jié)果。當(dāng)然也會有.catch方法
pms().then(res => {
console.log(res) // 'abc'
}).catch(e => {
console.log('錯(cuò)誤')
})
?
# await - 暫停異步函數(shù)的執(zhí)行
? await,即:async wait,旨在等待異步執(zhí)行結(jié)束,使用于async函數(shù)內(nèi)部。異步未執(zhí)行結(jié)束,阻塞當(dāng)前代碼。嗯哼?阻塞?。。?/strong>
? 與線程阻塞不同的是,await 的阻塞發(fā)生在 async 函數(shù)內(nèi)部,可以理解為一個(gè)異步的阻塞。跟在await后的JS表達(dá)式,可以等待很多類型的事件,但初衷是用于等待Promise對象。如果await的對象是promise對象,則阻塞異步函數(shù)代碼執(zhí)行等待promise的resolve狀態(tài),如果是同步執(zhí)行的表達(dá)式則立即執(zhí)行。
?
# 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() // 執(zhí)行立即打印 normal result
const res = await asyncFunc(1000)
console.log(res) // 執(zhí)行1s后打印 ‘a(chǎn)sync result’
}
awaitDemo() // 執(zhí)行
? 以上的例子比較常規(guī),再看看下面這個(gè)例子
async function func() {
console.log('123')
}
async function run() {
const res = await func()
console.log(res) // 123
}
run()
? 該例子中,func看似一個(gè)普通函數(shù),但經(jīng)async定義后,會返回一個(gè)promise對象,此時(shí)的await等待的就是該promise對象的resolve參數(shù),即123。
? 對比上一個(gè)例子中的normalFunc,主要有兩個(gè)理解點(diǎn)
(1)增加了async的普通函數(shù)變成了一個(gè)異步函數(shù),await等待的對象為promise對象,返回resolve參數(shù)
(2)await可以等待任何東西,如果等待的是普通內(nèi)容,則直接返回該內(nèi)容
?
# 【填坑1】異常處理
? 細(xì)心的你應(yīng)該已經(jīng)發(fā)現(xiàn),await 返回的是promise的resolve參數(shù),但對于catch卻沒有實(shí)際的處理。那當(dāng)我們的請求發(fā)生異常時(shí),該怎么辦?答案是使用try-catch,在Promise中的.catch()分支會流入catch中
function asyncFunc(time) {
return new Promise((resolve, reject) => {
setTimeout(() => { // 用setTimeout模擬異步
reject('promise reject')
}, time)
})
}
// 為了處理Promise.reject 的情況我們應(yīng)該將代碼塊用 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,執(zhí)行會報(bào)錯(cuò)
?
# 【填坑2】并行請求
? 對于一個(gè)真實(shí)的業(yè)務(wù)需求,通常會有多個(gè)異步請求需要同時(shí)執(zhí)行,如獲取左側(cè)目錄樹,和登錄賬戶的用戶信息等。因await遇到異步會阻塞,當(dāng)一個(gè)async函數(shù)內(nèi)有多個(gè)異步函數(shù)需要調(diào)用時(shí),就會出現(xiàn)相互等待的現(xiàn)象。天哪,“異步” 變成了 “同步”了,這不是我們所希望的
function asyncFunc() {
return new Promise((resolve, reject) => {
setTimeout(() => {
resolve('request done')
})
})
}
async function bugDemo() {
await asyncFunc()
await asyncFunc()
await asyncFunc()
console.log('finish')
}
? 以上代碼正常執(zhí)行了,沒有報(bào)出異常,但仔細(xì)觀察控制臺 timeline 可以發(fā)現(xiàn),三個(gè)函數(shù)是同步順序執(zhí)行的,其罪魁禍?zhǔn)拙褪?code>await的等待機(jī)制。那怎么解決這個(gè)問題?
? 其實(shí)async-await 只是promise的語法糖,但其并不能代替Promise,從對異常的處理上就可以看出,還需要引入try-catch ,本問題是另一個(gè)體現(xiàn)。解決辦法需要使用promise的.all()
promise.all( iterable ),當(dāng)所有請求都為resolve時(shí),返回resolve狀態(tài)
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 函數(shù)中即可,還必須在asyn函數(shù)的上下文中
// 雖在async函數(shù)里使用,但在forEach上下文中,異常
async function errorDemo() {
[1, 2, 3, 4, 5].forEach(item => {
await item;
});
}
errorDemo() // SyntaxError: await is only valid in async function
// await 必須在async函數(shù)的上下文中
async function correctDemo() {
let arr = [1, 2, 3, 4, 5];
for (let i = 0; i < arr.length; i ++) {
await arr[i];
}
}
?