JS中調用 async 函數時會返回一個 Promise 對象(隱式轉換)。當async 函數返回一個值時,Promise 的 resolve 方法會負責傳遞這個值,當 async 函數拋出異常時,Promise 的 reject 方法也會傳遞這個異常值。
在async 函數中如果遇見 await 表達式,則 async 函數會暫停執行,等待表達式中的 Promise 解析完成後繼續執行 async 函數並返回結果。
基於上面的定義,我們來對比一下如下的代碼,我們就知道優化點如何排查了。
function resolveAfter2Seconds(x) {
return new Promise(resolve =>setTimeout(() => resolve(x), 2000))
}
async function fn1(x) {
var a = resolveAfter2Seconds(20)
var b = resolveAfter2Seconds(30)
return x + await a + await b
}
// prints 60 after 2 seconds
fn1(10).then(v => console.log(v))
async function fn2(x) {
var a = await resolveAfter2Seconds(20)
var b = await resolveAfter2Seconds(30)
return x + a + b
}
// prints 60 after 4 seconds
fn2(10).then(v => console.log(v))
上面的代碼演示代碼中,我們可以發現,不同的 await 位置聲明,會直接影響到函數執行的時間。
同時,我們在處理 async 函數的返回時,針對其 reject 的場景時,try-catch 模塊來進行捕獲,但由於try-catch會創建獨立的作用域(這算是很老的一個點了,不知新版的V8有沒有進行過優化),所以會在性能上有一些損失,但是相對於代碼的可讀性,這一點損失也無所謂啦。
async function fn(url) {
let v
try {
v = await doSomething(url)
} catch (e) {
v = await handlerError(url)
}
return success(v)
}
// 比價醜陋的寫法,通過犧牲代碼的可讀性來避免作用域的開闢
async function fn(url) {
let v
v = await doSomething(url).catch(err => {
v = await handlerError(url)
})
return success(v);
}