await-to-js 原始碼分析,體驗一把捕獲異常的優雅
theme: cyanosis highlight: atelier-dune-light
前言
await-to-js 的原始碼較少,程式碼較少的情況,可以嘗試採用 3W 方法進行原始碼分析,幫助快速瞭解它的實際應用場景。
閱讀本文,會有以下收穫:
- 瞭解 await-to-js 庫能解決什麼問題。
- 分析 await-to-js 庫是如何解決問題。
- 摸索 await-to-js 庫的實際使用場景。
Why:await-to-js 誕生的契機
開啟它的 github 地址,有很簡單的一句介紹:
Async await wrapper for easy error handling
這句話的意思是:
Async await 包裝器,便於錯誤處理
等等,async 和 await 怎麼捕獲異常來著。
常規異常捕獲
一般情況下,await 命令後面是一個 Promise 物件,返回該物件的結果。
如果我們希望捕獲 Promise 中的錯誤, 這個時候需要將 await 放在try...catch結構裡面。
同時,這樣做,無論前面的 await 是否成功,都不會影響後續的功能。
功能設計
該功能設計源自原始碼的 examples 檔案中提供的案例,我將它改成了使用 await-to-js 前的寫法。
- UserModel:提供了 findById 方法,是一個 Promise,它主要做了兩件事,如果 userId 存在,則返回 userObjet 物件,如果 userId 不存在,則用 reject 丟擲錯誤。
- asyncTask: 兩個引數,userId 和結果返回函式。使用async / await 處理非同步操作 UserModel,將 await 放在 try...catch 結構裡面。如果 UserModel 正常,則判斷是否查到了 userId,如果 UserModel 阻塞,也不影響後面程式碼的執行。
- 呼叫 asyncTask 的時候,第一個引數傳 null 。
``` const UserModel = { findById: userId => { return new Promise((resolve, reject) => { if (userId) { const userObjet = { id: userId, notificationsEnabled: true, };
return resolve(userObjet);
}
reject('Data is missing');
});
}, };
/* * 非同步任務 / async function asyncTask(userId, cb) { try { const user = await UserModel.findById(userId); if (!(user && user.id)) return cb('No user found'); } catch () {; } return cb('前面的 await 可能失敗了'); }
asyncTask(null, (err, newTask) => { console.log('fail'); console.log(err); console.log(newTask); }); ```
執行結果
因為 userId 的值為 null,所以 UserModel.findById 丟擲了異常,而我在 catch 中沒有做任何處理,所以後面的程式碼正常執行,將最後的程式碼處理結果拋給了 cb 函式。
所以,最終列印 cb 裡的結果:
捕獲異常
如果需要進行異常的捕獲,在 catch 中將錯誤返回即可。
async function asyncTask(userId, cb) {
try {
const user = await UserModel.findById(userId);
if (!(user && user.id)) return cb('No user found');
} catch (e) {
return cb(e);
}
return cb('前面的 await 可能失敗了');
}
執行結果
此時返回的報錯內容為: Data is missing
契機
上面的例子中,雖然程式碼執行正常,也可以進行異常的捕獲。
但是有個問題,如果想捕獲不同場景下的異常,需要將每個場景都放到 try...catch 結構中,這樣程式碼會顯得有些冗餘。
能否,將 try...catch 結構封裝起來,直接返回結果?
這不,await-to-js 就誕生了麼。
What:如何優雅捕獲異常
開頭說原始碼內容少,並不是說說看,它是真的少。
核心原始碼
原始碼用的 TypeScript,因為程式碼量不多,所以沒有用過 TypeScript 的開發者,也可以很順暢的閱讀。
```
/*
* @param { Promise } promise
* @param { Object } errorExt - Additional Information you can pass to the err object
* @return { Promise }
/
export function to
return [err, undefined];
});
}
export default to; ```
不過,想看用 JavaScript 寫的也不難,將專案 build 一下,就能得到。順便在上面加了些註釋:
``` /* * @param { Promise } promise * @param { Object } errorExt - Additional Information you can pass to the err object * @return { Promise } / function to(promise, errorExt) { return promise .then(function (data) { // 返回陣列值:第一個元素是錯誤資訊(只有報錯時有值),第二個元素是資料資訊(只有成功時有值) return [null, data]; }).catch(function (err) { // =>true: 如果添加了額外的錯誤描述,則和錯誤描述合到一個空物件上 if (errorExt) { var parsedError = Object.assign({}, err, errorExt); return [parsedError, undefined]; } return [err, undefined]; }); }
export { to }; export default to; ```
這裡需要注意下面幾點:
- errorExt 是 Object 型別。
- Object.assign 在進行拷貝的時候,會把非 Object 型別轉成 Object 型別。
``` const err1 = 'time'; const err2 = 's';
const task = Object.assign({}, err1, err2); console.log(task); // { '0': 's', '1': 'i', '2': 'm', '3': 'e' } ```
疑問
關於 errorExt 我是有疑問的,它的結果到底怎麼回顯?
原始碼中,promise 中的 reject 丟擲的都是字串。上面提到了,Object.assign 會將字串轉成 Object 型別,得到的結果不是很直觀。
來個例子看看執行結果:
``` async function asyncTask(userId, cb) { let err, user, savedTask, notification; [err, user] = await to(UserModel.findById(userId), { errorExt: ' ext error' }); if (err) return cb(err);
cb(null, savedTask); } asyncTask(null, (err, newTask) => { console.log('fail'); console.log(err); console.log(newTask); }); ```
執行結果
這個結果並不直觀
想要更直觀的內容,只能將結果進行一次特殊處理,比如物件迴圈,只展示 value 值
if (err) {
let errList = Object.values(err);
let errStr = errList.join('');
return cb(errStr);
}
執行結果
這次列印的內容直觀多了。
關於疑問的部分,僅個人想法,歡迎留言討論
示例演示
還是上面的例子,這次使用 await-to-js 捕獲異常。只需要一行程式碼,便可輕鬆捕獲異常。
``` import to from '../../dist';
async function asyncTask(userId, cb) { let err, user, savedTask, notification; [err, user] = await to(UserModel.findById(userId)); // 直接捕獲異常 if (err) return cb(err); }
asyncTask(null, (err, newTask) => { console.log('fail'); console.log(err); console.log(newTask); }); ```
執行結果
How:使用場景小結
不僅可以直接捕獲異常,還可以根據資料的值進行特殊的邏輯處理,幫助完成一些複雜邏輯。
``` async function asyncTask(userId, cb) { let err, user, savedTask, notification; [err, user] = await to(UserModel.findById(userId)); if (!(user && user.id)) return cb('No user found');
cb(null, savedTask); } ```
執行結果
在上面的程式碼中,根據得到的 user 的值,進行判斷,在實際開發中的實用性更強一些。
總結
await-to-js 原始碼讀完,個人感覺實際用起來,程式碼會寫的比較優雅。而且它的原始碼內容不多,簡單易讀。
簡單總結一下本文收穫:
1、使用 await-to-js,可以幫助改善非同步捕獲異常的冗餘寫法。
2、原始碼的程式碼量不多,可以作為工具類放到專案中使用。
3、在日常開發中,可以留意一下類似的功能改造,減少重複寫法。
以上就是本次分享的內容。如果覺得有幫助,歡迎留言討論、點贊 、收藏,持續產出技術分享。
我是 葉一一,非職業「傳道授業解惑」的技術博主。「趣學前端」、「CSS暢想」系列作者。
華夏美食、國漫、古風重度愛好者,刑偵、無限流小說初級玩家。
歡迎技術或非技術問題的討論。
本文正在參加「金石計劃」
- await-to-js 原始碼分析,體驗一把捕獲異常的優雅
- CSS偽類的第三集,原來偽類也可組CP
- 從:is()說起,開啟CSS偽類第二集
- 一組純CSS開發的聊天背景圖,幫助避免發錯訊息的尷尬
- 「CSS特效」我的發呆專屬,反覆解鎖手機螢幕
- 「技術分享」以Antd為例,快速打通UI元件開發的任督二脈
- 「功能實現」我封裝了一個表單元件,感覺離財富自由又近了一步
- 「經驗總結」高效開發,老程式碼可以這樣動
- 前端開發提效小技巧之業務功能篇
- 人生有忙忙碌碌,也有詩和遠方 | 2022年中總結
- 【端午節】新奇體驗,我用react實現網頁遊戲的全過程(包括規則設計)
- 【暑假記憶】消暑神器,我用CSS復刻了一個遊戲機
- 突圍?我願稱之為向上的攀登者
- 【孟夏之遇】望孟夏之短夜兮,螢星相伴
- 【技術學習】SVG-邊學邊做
- 【TS實踐】自己動手豐衣足食的TS專案開發
- 【碼上掘金】通過FileReader讀取Excel檔案內容
- 【Taro開發】四月芳菲,Taro觀賞指南
- 【Node.js】青梅煮酒,聊聊zlib壓縮
- 【知識點】關於iframe跨域通訊