無痛處理async-await的異常,告別一個請求一個try-catch----Axios示例
async-await的痛點
async-await搭檔雖然也是返回Promise物件,但無法做到.then
與.catch
那樣拿到Promise中reject的結果以及其中丟擲的Error。
換句話說,我們用async-await來寫非同步請求很多時候會這樣:
ts
async function getUserInfo() {
try {
const res = await fetchUserInfo(id);
} catch(err) {
// 如果你用了axios攔截器,這裡會是攔截器reject的結果
// 只有通過try-catch你才能拿到這個錯誤
}
// 其他的業務程式碼
}
很多時候我們在axios攔截器的內部統一處理了錯誤,並使用一些UI框架的訊息彈出功能,將錯誤顯示給使用者,這個邏輯是沒問題的,但會有一些需要特殊處理的邏輯。例如載入動畫的控制:
ts
async function getTableData() {
this.loading = true;
try {
const res = await fetchTable({ page, size });
} catch(err) {
// 如果你用了axios攔截器,這裡會是攔截器reject的結果
// 只用通過try-catch你才能拿到這個錯誤
} finally {
this.loading = false;
}
}
這是非常常見的業務邏輯,不管是報錯還是正常,你需要在介面給你迴應後將loading效果給去掉。因此這種情況是必須使用try-catch的,另外還有一些諸如:
- 請求異常後回到上一頁面。
- 請求異常後清空某些資料。
- 請求異常後再次發起請求。
太多了,這類情況在業務中很常見,這裡只是舉了幾個例子。那麼對於這些情況,如果使用async-await的請求風格,我們就必須每個請求都包一個try-catch,這是很麻煩的。
使用Proxy代理非同步函式
我使用的axios,下面用axios來做為例子。
利用Proxy的apply
陷阱攔截函式執行,並在其中統一try-catch:
```ts
// 在utils檔案中我們編寫了一個函式。使用代理模式的思想,我們在其中統一處理了異常
export function createErrorProxy
return proxyFn;
}
接下來,需要做的就是重寫axios的幾個例項方法:
ts
import axios from "axios";
import { createErrorProxy } from "@/utils";
const axiosInstance = axios.create({/ config /}); // 攔截器之類的程式碼 // ... // 使用Proxy重寫post等你用得上的請求方法 ["post", "get", "delete", "put"].forEach(type=> { axiosInstance[type] = createErrorProxy(axiosInstance[type]); });
export default axiosInstance;
到這裡其實就搞定了,下面是非常常見的api封裝:
ts
// 這塊就是剛才我們處理過的axios
import request from "@/request";
// api檔案下的使用者資訊請求模組
export function fetchUserInfo(id: string): Promise/system/user/${id}
);
}
// ....
```
匯入使用時就不再需要try-catch包裹了。
總結
相較於.then
和.catch
,我更喜歡使用async-await搭檔,這對基於Generator
的關鍵字讓非同步程式碼能夠以同步的編碼方式書寫出來,搭配解構與Error-First是我最喜歡的請求風格:
ts
async function getTableData() {
const [error, userInfo] = await fetchUserInfo(id);
if (error) {
// 錯誤的處理
return;
}
// 正常情況
userModule.$patch({ userInfo });
}
你要說它比.then
和.catch
少寫了程式碼嗎,似乎沒有,這就看個人喜好了。
回到正題,經過代理後的axios請求已經統一處理了異常,後續的所有請求都不用再一次一次的重複寫try-catch,我們可以放心的在await後面做一些事情。
這篇文章很短,但希望能給大家一點幫助,如果有錯誤或者建議歡迎各位大佬在評論區批評指正。