第一篇認識 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 程式導師實驗計畫,讓我自學的路更完整更有系統。