1. 程式人生 > 實用技巧 >你可能忽略的 async/await 問題

你可能忽略的 async/await 問題

一個例子

下面是一個常見的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都可以並行執行了。