你可能忽略的 async/await 問題
阿新 • • 發佈:2020-10-20
一個例子
下面是一個常見的vue程式碼片段:
async initStore(query) {
await this.getConfig();
await this.getUser();
await this.checkRussianContext(query);
await this.getBasket(this.$store.state.config.selectedCurrency),
await this.$store.dispatch('options/fetchOptions', {
basket : this.$store.state.basket,
});
},
上面的程式碼中,每一行都會等待上一行的結果返回後才會執行。比如其中的getUser會等待getConfig資料返回之後才會執行。
當看到這段程式碼時,腦子裡應該注意到以下幾點:
- 如果某一行的執行不需要上面一行的資料怎麼辦?為什麼阻塞程式碼使整個應用程式變慢呢?
- 我們可不可以讓所有不相關的方法一起執行?比如使用Promise.all?
- 能否讓相關的方法使用then進行鏈式呼叫來避免阻塞剩餘程式碼?
本篇文章的重點就是通過分析async/await可能引發的問題,幫你找到程式碼的壞味道。
無關的請求請並行執行
讓我們來看一些具體的資料下的情況。
下面是示例程式碼:
const getUserData = async () => {
// 獲取一張隨機的狗狗圖片作為頭像
const res = await fetch('https://dog.ceo/api/breeds/image/random')
const { message } = await res.json()
// 獲取隨機生成的使用者資訊
const user = await fetch('https://randomuser.me/api/')
const { results } = await user.json()
// ...
}
上面的程式碼在fast 3G(使用 Chrome 開發者工具)下執行 100 次,平均執行時間為1231.10ms
但是很顯然,第二個請求並不需要第一個請求的結果,所以我們修改成以下程式碼並執行 100 次:
const getUserDataFaster = async () => {
// 兩個請求並行執行
const [res, user] = await Promise.all([
fetch('https://dog.ceo/api/breeds/image/random'),
fetch('https://randomuser.me/api/')
])
const [{ message }, { results }] = await Promise.all([res.json(), user.json()])
// ...
}
我們得到的平均執行時間為612.50ms,幾乎節省了一半時間。
劃重點:儘可能地把查詢請求並行執行。
可以用這個codepen中的程式碼體驗
廣州設計公司https://www.houdianzi.com 我的007辦公資源網站https://www.wode007.com
無關的程式碼你不必等
再來例子:
async initStore(query) {
await Promise.all([
this.getConfig(),
this.getUser(),
this.checkRussianContext(query)
])
await this.getBasket(this.$store.state.config.selectedCurrency),
await this.$store.dispatch('options/fetchOptions', {
basket : this.$store.state.basket,
});
await initBooking()
},
前面的 3 個請求是並行執行的,而下一段程式碼依賴了前面獲取的資料,所以需要在其後執行,但是你有沒有發現其中的問題?
initBooking這個小可憐只能等到getBasket和fetchOptions完成之後才能執行,儘管它不需要這兩個方法的任何資料。
一個簡單的解決辦法是將await換成.then來使用:
關於這個用法可以看開頭的另一篇文章
async initStore(query) {
await Promise.all([
this.getConfig(),
this.getUser(),
this.checkRussianContext(query)
])
this.getBasket(this.$store.state.config.selectedCurrency).then(async () => {
await this.$store.dispatch('options/fetchOptions', {
basket : this.$store.state.basket,
});
})
await initBooking()
},
這樣的話,getBasket和initBooking都可以並行執行了。