優雅地 `async/await`
async/await
雖然取代了回撥,使用類似同步的程式碼組織方式讓程式碼更加簡潔美觀,但錯誤處理時需要加try/catch
。
比如下面這樣,一個簡單的 Node.js 中使用async/await
的場景:
const fetch = require("node-fetch"); async function getData() { const url = "https://api.github.com/users/wayou"; try { const response = await fetch(url); const data = await response.json(); console.log(data); } catch (error) { throw error; } } getData();
像這樣的非同步場景,Node.js 中會有很多。如果都通過try/catch
來錯誤處理,數量多了之後也是不太美觀的。
將非同步進行一層封裝
因為本質上async/await
是Promise
,我們可以封裝一個簡單的方法,將錯誤處理變得更優雅。
比如下面這樣:
function await2js(promise) { return promise.then(result => [undefined, result]).catch(error => [error, undefined]); }
該方法始終返回兩個結果,第一個是錯誤,第二個是資料,這和 Node.js 中回撥的入參(err,data)=>void
是一致的,使得這層包裝很 Node.js,一點也不會感到奇怪。
所以改造後的使用示例:
async function getData() { const url = "https://api.github.com/users/wayou"; const [error, response] = await await2js(fetch(url)); if (error) { throw error; } const [error2, data] = await await2js(response.json()); if (error2) { throw error2; } console.log(data); }
這層封裝針對單個await
,不像try/catch
那麼粗獷一下子包含再次await
。也不是說try/catch
不能精細地處理錯誤,但腦補一下把上面兩次await
都用try/catch
的模樣。
當然,如果嫌麻煩,也可通過Promise.all()
或 Promise 的鏈式呼叫將多次await
操作合併,只處理一次錯誤。
TypeScript 版本
function await2js<T, K = Error>(promise: Promise<T>) { return promise .then<[undefined, T]>((response: T) => [undefined, response]) .catch<[K, undefined]>((error: K) => [error, undefined]); }
這裡有個相應的 npm 包便是做這事情的await-to-js 。