譯者按: 使用.catch()
來捕獲全部的異常javascript
本文采用意譯,版權歸原做者全部html
async/await 中的異常處理很讓人混亂。儘管有不少種方式來應對async 函數的異常,可是連經驗豐富的開發者有時候也會搞錯。java
假設你有一個叫作run()
的異步函數。在本文中,我會描述 3 種方式來處理run()
的異常情形: try/catch
, Go 語言風格, 函數調用的時候使用 catch()
(即run().catch()
)。 我會跟你解釋爲何其實幾乎只須要catch()
就足夠。node
try/catch
當你第一次使用async/await
, 你可能嘗試使用try/catch
將每個 async 操做包圍起來。若是你await
一個被 reject 的 Promise,JavaScript 會拋出一個能夠被捕獲的錯誤。golang
run();
async function run() {
try {
await Promise.reject(new Error("Oops!"));
} catch (error) {
error.message; // "Oops!"
}
}
複製代碼
try/catch
可以捕獲非異步的異常。編程
run();
async function run() {
const v = null;
try {
await Promise.resolve("foo");
v.thisWillThrow;
} catch (error) {
// "TypeError: Cannot read property 'thisWillThrow' of null"
error.message;
}
}
複製代碼
因此,只須要將全部的代碼邏輯都用 try/catch
包圍起來就能夠搞定?也不徹底正確。下面的代碼會拋出unhandled promise rejection. await
將一個被拒絕的 promise 轉換爲可捕獲的錯誤,可是 return
不行。小程序
run();
async function run() {
try {
// 注意這裏是return,不是await
return Promise.reject(new Error("Oops!"));
} catch (error) {
// 代碼不會執行到這裏
}
}
複製代碼
也不可能使用 return await
來繞開。微信小程序
還有一個缺點就是使用了try/catch
以後,就很難用.
的語法來進行 Promise 鏈式組合了。數組
另外一個常見的方式就是使用then()
將一個原本須要用catch()
來捕獲並處理的 Promise 轉換爲普通的 Promise。而後像 Go 語言中同樣,使用if(err)
來處理異常。promise
run();
async function throwAnError() {
throw new Error("Oops!");
}
async function noError() {
return 42;
}
async function run() {
// The `.then(() => null, err => err)` 來匹配正常/異常的狀況。若是正常狀況,返回`null`;若是異常,返回`err`
let err = await throwAnError().then(() => null, err => err);
if (err != null) {
err.message; // 'Oops'
}
err = await noError().then(() => null, err => err);
err; // null
}
複製代碼
若是你真的想要同時返回 error 和正確的值,你能夠徹底僞裝在用 Go 語言。
run();
async function throwAnError() {
throw new Error("Oops!");
}
async function noError() {
return 42;
}
async function run() {
// The `.then(v => [null, v], err => [err, null])` pattern
// 你能夠使用數組解構來匹配err和返回值
let [err, res] = await throwAnError().then(
v => [null, v],
err => [err, null]
);
if (err != null) {
err.message; // 'Oops'
}
err = await noError().then(v => [null, v], err => [err, null]);
err; // null
res; // 42
}
複製代碼
使用 Go 語言風格的錯誤處理並不能擺脫return
沒法捕獲的狀況。並且還讓整個代碼更加的複雜,若是忘記if(err != null)
,就會出問題。
總的來講,有兩大缺點:
if (err != null)
,真的很累,並且容易漏掉;run()
函數中的非異步的錯誤也沒法處理;總的來講,它並無比try/catch
好多少。
catch()
try/catch
和 Go 語言風格的異常處理都有各自的使用場景,可是處理全部異常最好的方法是在run()
函數的後面使用catch()
,像這樣:run().catch()
。換句話說,用一個catch()
來處理run
函數中的全部錯誤,而不是針對run
裏面的每一種狀況都去寫代碼作相應的處理。
run()
.catch(function handleError(err) {
err.message; // Oops!
})
// 在handleError中處理全部的異常
// 若是handleError出錯,則退出。
.catch(err => {
process.nextTick(() => {
throw err;
});
});
async function run() {
await Promise.reject(new Error("Oops!"));
}
複製代碼
記住,async 函數老是返回 promise。只要函數中有異常,Promise 會 reject。並且,若是一個 async 函數返回的是一個 reject 的 Promise,那麼這個 Promise 依然會繼續被 reject。
run()
.catch(function handleError(err) {
err.message; // Oops!
})
.catch(err => {
process.nextTick(() => {
throw err;
});
});
async function run() {
// 注意:這裏使用了return,而不是await
return Promise.reject(new Error("Oops!"));
}
複製代碼
爲何使用run().catch()
而不是將整個run()
函數用try/catch
包起來呢?咱們首先來考慮一個狀況:若是try/catch
的catch
部分有異常,咱們應該如何處理呢?只有一個方法:在catch
裏面接着使用try/catch
。因此,run().catch()
的模式使得異常處理變得很是簡潔。
咱們最好是全局的有一個 errorHandler 來處理那些沒有考慮到的異常,好比使用run().catch(handleError)
,而不是在run()
函數裏面全部可能出錯的地方加上try/catch
。
Fundebug專一於JavaScript、微信小程序、微信小遊戲、支付寶小程序、React Native、Node.js和Java線上應用實時BUG監控。 自從2016年雙十一正式上線,Fundebug累計處理了10億+錯誤事件,付費客戶有陽光保險、核桃編程、荔枝FM、掌門1對一、微脈、青團社等衆多品牌企業。歡迎你們免費試用!
轉載時請註明做者 Fundebug以及本文地址:blog.fundebug.com/2019/07/24/…