4 - 非同步之認識 async/await


Posted by backas36 on 2021-07-21

第一篇認識 XMLHttpRequest, Promise,
第二篇了解 Promise 串 API,
第三篇介紹 fetch 串 API,
這裡,終於最後一篇了,要來教比較輕鬆的 async & await。

可以先參考 MDN 給的解釋:
MDN async function

其中裡面有提到 『 async/await 函式的目的在於簡化同步操作 promise 的表現,以及對多個 Promise 物件執行某些操作。』

所以我們可以利用 async 來對上一篇的 fetch 進行簡化。

首先來個簡單的範例,新建一個 function getDataFromServer,把剛剛的 fetch 包進去進行改寫,並且呼叫 getDataFromServer

 const api200 = 'https://run.mocky.io/v3/98c6ea3a-2359-429d-8be1-98396934af0f'
    // 完全正常運作的 server

const api500 = 'https://run.mocky.io/v3/bf2b926c-0db4-4f98-b09a-1211d4da6038'
// 假裝 server error 回傳 500 的 api

const apiJsonError = 'https://run.mocky.io/v3/3bf6bbd8-6b7c-47f7-8b92-9efb2a0a8dfc'
// 回傳 200 但是 json 格式錯誤的 api

const apiWrongUrl = 'https://srun.mocky.io/v3/98c6ea3a-2359-429d-8be1-98396934af0f'

const apiUser = 'https://reqres.in/api/users'


// 1. 先進一個 async function
const getDataFetch = async (apiURL) => {
  const response = await fetch(apiURL)
  const data = await response.json() 
  //因為 response.json()回傳Promise所以要加上 await

  console.log(data)
}

getDataFetch(api200)

有沒有看起來就是像一般的 function!!! 但是我必須加入兩個關鍵字 async 需加在宣告 function 那行,你也可以不要使用箭頭函示:

async function getDataFetch(apiURL) {
  //...省略
}

然後記得,會回傳 Promise 的 fetch(apiURL)response.json(),需要在前面加上 await 才可以完成這個非同步 function。

接著,我們加上錯誤處理,在非同步 function 裡面,我們需要使用 try catch,來處理 Promise 的 reject 情形。

// ...以上省略

const getDataFetch = async (apiURL) => {
  try {
    const response = await fetch(apiURL)
    if (response.ok) {
      const data = await response.json()
      console.log(data)
      return
    }
    throw new Error('HTTP 500/400 Error')
    return
    } catch (error) {
    console.log(error)
    return
    }

}

getDataFetch(api500)

目前為止就是完整的 async/await 串 API 加上處理錯誤的示範了。

沒沒沒,還沒完,實際上我們是常需要非同步function 回傳 response 資料的,但是因為這個不是一般 function,所以你像以下去執行的話,會發現回傳的並不是你想的那樣。

const getDataFetch = async (apiURL) => {
  try {
    const response = await fetch(apiURL)
    if (response.ok) {
      const data = await response.json()
      return data
    }
    throw new Error('HTTP 500/400 Error')
    return
  } catch (error) {
    console.log(error)
    return
  }

}

const data = getDataFetch(api200)
console.log(data) // Promise

執行後,得到回傳並不是 response 資料,而是一個 Promise。

想起來了嗎... 因為是 Promise 所以我要使用 then 去拿到資料!
所以現在改寫成這樣:

// ... 以上省略
getDataFetch(api200).then(data => {
  console.log(data) //成功拿到
})

如果不想要使用 then 拿到資料,那我們就需要使用到 IIFE (Immediately Invoked Function Expression) 這個概念去改寫:

// ... 以上省略
(async () => {
  let data = await getDataFetch(api200)
  console.log(data)
})()

那現在如果我們想要串多個 API 呢?
來吧。

const getDataFetch = async () => {
  try {
    const response = await fetch(api200)
    const response2 = await fetch(apiUser)

    if (response.ok) {
      const data = await response.json()
      const data2 = await response2.json()

      return [data, data2]
    }
    throw new Error('HTTP 500/400 Error')
    return
  } catch (error) {
    console.log(error)
    return
  }
}
getDataFetch().then(data => {
  console.log(data)
})

這樣我們就可以成功串一個以上的 API 了,看起來是不是比就像平常在寫 function 一樣,這就是 async 的偉大之處,它讓我們可以寫異步 function 像在寫同步 function 一樣。

但是這邊有個小問題,就是當 fetch 多個 API 的時候,假設一個 API 要等兩秒,那麼這個範例就會要等4秒執行完成,我們必須要使用 Promise.all 來讓他們並行執行。

 const getDataFetch = async () => {
      try {
        const responses = await Promise.all([fetch(api200), fetch(apiUser)])
        const datasPromise = responses.map(response => {
          if (!response.ok) {
            throw new Error('HTTP 500/400 Error')
          }
          return response.json()
        })
        const datas = await Promise.all(datasPromise)
        return datas
      } catch (error) {
        console.log(error)
        return
      }
    }
    getDataFetch().then(datas => {
      console.log(datas)
    })

記得在 async function 裡面使用到 Promise 就必須加上 awiat。
這個範例是先把需要串的 API 包成一個陣列,並用 Promise.all 去執行,接著再利用 map 方式去將每個 fetch 的 response 用 .json() 去解析。
接著再使用一次 Promise.all 去跑一次 datasPromise 放到 datas,接著回傳就可以拿到資料了。

Promise.all 只要裡面任何一個 Promise 解析成 reject,那整個 Promise.all 就會是 reject 狀態。

這邊就不在多講 Promise 的用法了,講不完!改天再來研究!
以上就是這非同步方法的最後一篇了,希望對你和我自己都有幫助。


原來寫文章這麼不容易。😥

主要是因為自己對 Promise, fetch, async, await 非常的陌生,本來想用筆記的方式記錄下來,但是通常我的筆記非常的雜亂,常常是寫過記錄過就忘記這回事,就算認真找出來看,其實也看不太懂當初在筆記些什麼。

今天藉由寫文章的方式一方面是希望自己更有動力去找出細節,另一方面也是強迫自己更熟悉它們。

如果以上有哪邊提供了錯誤資訊,記得告訴我!
辛苦大家的閱讀了,我們一路上一起加油 🚀

1 - 非同步之認識Promise
2 - 非同步之Promise串 API
3 - 非同步之認識 fetch
4 - 非同步之認識 async/await

還有,感謝 Huli 的 Lidemy 程式導師實驗計畫,讓我自學的路更完整更有系統。


#非同步 #javascript #Async







Related Posts

立即執行函式(IIFE)的理解與運用

立即執行函式(IIFE)的理解與運用

[FE302] React 基礎 - hooks 版本:state

[FE302] React 基礎 - hooks 版本:state

For 大家族 for 迴圈、forEach、for...in、for...of 一次搞清楚

For 大家族 for 迴圈、forEach、for...in、for...of 一次搞清楚


Comments