一、Async 函數的錯誤處理
async 函數的語法不難,難在錯誤處理上。先來看下面的例子:
我們可以看到 Promise 報錯后,a = await 1 並沒有被執行。即當 async 函數中只要一個 await 出現 reject 狀態,則后面的 await 都不會被執行。
解決辦法是:可以添加 try catch。
// 正確的寫法
let a; async function correct() { try { await Promise.reject('error') } catch (error) { console.log(error); } a = await 1; return a; } correct().then(v => console.log(a));
這樣就會先打印 error,再打印 1。因此,如果有多個 await
則可以將其都放在 try/catch
中,但很顯然,這樣並不優雅。
// 我們需要對每一次異步操作進行錯誤處理
function async asyncTask(cb) { try { const asyncFuncARes = await asyncFuncA() } catch(error) { return new Error(error) } try { const asyncFuncBRes = await asyncFuncB(asyncFuncARes) } catch(error) { return new Error(error) } try { const asyncFuncCRes = await asyncFuncC(asyncFuncBRes) } catch(error) { return new Error(error) } }
二、npm包:await-to-js 及源碼分析
作者是這樣介紹這個庫的:Async await wrapper for easy error handling without try-catch。中文翻譯過來就是:無需 try-catch 即可輕松處理錯誤的異步等待包裝器。
與上面對比,使用了await-to-js之后,我們可以這樣的處理錯誤:
import to from './to.js'; function async asyncTask() { const [err, asyncFuncARes] = await to(asyncFuncA()) if(err) throw new (error); const [err, asyncFuncBRes] = await to(asyncFuncB(asyncFuncARes)) if(err) throw new (error); const [err, asyncFuncCRes] = await to(asyncFuncC(asyncFuncBRes) if(err) throw new (error); }
簡潔多了,看看源碼,沒寫想到只有 15 行,完全都可以自己作為一個公共方法使用即可。
export function to<T, U = Error> ( promise: Promise<T>, errorExt?: object ): Promise<[U, undefined] | [null, T]> { return promise .then<[null, T]>((data: T) => [null, data]) .catch<[U, undefined]>((err: U) => { if (errorExt) { const parsedError = Object.assign({}, err, errorExt); return [parsedError, undefined]; } return [err, undefined]; }); } export default to;
上面是TS版的源碼,但是考慮到有些同學可能還沒接觸過TS,我着重分析一下下面這版JS版的源碼。
export function to(promise, errorExt) { return promise .then((data) => [null, data]) .catch((err) => { if (errorExt) { const parsedError = Object.assign({}, err, errorExt); return [parsedError, undefined]; } return [err, undefined]; }); } export default to;
這里我們先拋開 errorExt 這個自定義的錯誤文本,可以看出:其代碼的邏輯用中文解釋是這樣的:
- 無論成功還是失敗,都返回一個數組,數組的第一項是和錯誤相關的,數組的第二項是和響結果相關的
- 成功的話,數組第一項也就是錯誤信息為空,數組第二項也就是響應結果正常返回
- 失敗的話,數組第一項也就是錯誤信息為錯誤信息,數組第二項也就是響應結果返回undefined
經過上面的分析發現:其實挺簡單的,但是,又確實挺巧妙的,我們也並不是做不到,而不是想不到。
這里我們再來看函數 to 的第二個參數 errorExt,不難發現這其實就是拿來用戶自定義錯誤信息的,通過 Object.assign
將正常返回的 error 和用戶自定義的,合並到一個對象里面供用戶自己選擇。