錯誤處理往往是最容易被忽略的一塊,因為
這樣到底什麼時候要錯誤處理?
為了方便講解,我們先來複習一下錯誤處理的必須武器 try catch,如果你已經很熟了可以跳過這一段:
try
/catch
/finally
屬於流程控制的邏輯區塊(跟 if
/else
一樣),以下分別介紹:
openFile();
try {
writeFile(theData); // 可能產生例外
} catch(e) {
handleError(e); // 處理可能發生的例外
} finally {
closeFile(); // 總是在 try 結束後關閉檔案
}
try
throw
),就不會繼續往下執行,而是跳到 catch 區塊第一行開始執行。catch
finally
try
或 catch
區塊執行完就會進來,用來處理一些無論有沒有錯誤都要做的事情(比如把 loading 關掉、寫 log 等)。來談談錯誤的種類,了解一下「錯誤」到底是什麼?才會知道該怎麼處理它:
即程式本身的 bug,錯誤是程式本身沒寫對造成的。常見的例如:
這類型的錯誤代表與開發者的意圖背道而馳,因此沒有懸念,就是一定要把它改對,通常透過開發人員工具可以輕鬆找到原因。
在程式本身沒有 bug 的情況下,錯誤發生在系統本身,通常是程式與外部互動下發生,外部可能是使用者、網路遠端、檔案系統。常見的例如:
這類型的錯誤代表,通常在使用者「預想」情況下,程式是可以正常運作的,但是如果:
十筆資料可以跑,那十萬筆呢?
都市可以跑,那偏鄉地區呢?
json 檔案可以處理,那 xml 呢?
因此,其實這類型的錯誤不太算是「錯誤」,畢竟部分的 case 都還是可以運作的,或許比較適合稱呼為例外(exception)。
這類型的錯誤處理起來就複雜許多,需要對症下藥,
let file;
try {
file = readFile(filePath);
} catch (err){
file = createFile(filePath);
}
console.log(file);
let retryCount = 0;
const retryMax = 3;
do {
try {
console.log(retryCount);
// ...
// 可能失敗的 code 放這
// ...
break;
} catch (err){
console.log(err);
retryCount++;
if (retryCount >= retryMax) {
// 超過 retry 次數強制跳出
break;
} else {
// 還沒超過可以再試一次
continue;
}
}
} while (true);
setLoading(true);
try {
// ...
// 可能失敗的 code 放這
// ...
} catch (err){
logToDB(err);
} finally {
setLoading(false);
toast('系統不穩,請稍後再試');
}
未知的問題在 catch
處理很合理,有一些已知的問題也放在 catch
處理,但既然我都知道這邊有可能會出問題了,為何不乾脆用 if/else 判斷處理?
這個問題其實我也覺得稍微模糊,我的想法是,需要先去定義出,catch
究竟要接收什麼?是所有取不回資料的狀況嗎?還是針對非正常流程的處理?針對不同的目的,應該使用不同的處理,以下是我的一些想法
適合放在 catch
處理的:
fetch
資料途中,發生例外情況不適合放在 catch
處理的(可以用 if/else 處理):
false
,也就是使用者不授予權限時。但這也不是硬性規定,單純是不同的想法導致不同的設計,甚至不同的 API 在處理錯誤的狀態也都不盡相同,因此筆者認為,只要整個 app 在面對錯誤是一致就可以了 (比如查無資料要嘛都放 else
,要嘛都放 catch
)。
回歸到開頭的問題,為什麼錯誤處理往往是最容易被忽略的一塊?
類比來說,其實錯誤處理很像是現實社會中的「保險」:
當然啦,錯誤處理跟保險還是有很多細節、情境的不同,不可完全類比,這邊只是筆者有感而發XD,覺得有些事情,我們在寫程式的時候會碰到,在現實生活中也會碰到。
錯誤/例外處理是很多開發者不太想去碰的一塊,很容易只關注在「正常」,而比較少考慮「異常」。
市場上大部分的商業專案、產品,PM 當然也都是要求先有功能,畢竟沒功能的話,就算有一堆 try catch 也是枉然。
但正是因為「少而重要」,才能體現出價值,如果能夠在這點更謹慎,無疑是朝向「更好」的 developer 邁進!
愈深的洞穴裡
藏著愈閃耀的寶石
這讓我想到 笑談軟體工程:例外處理設計的逆襲 這本書的存在...
哇讓我來瞧瞧,看起來是整本書都充滿了,讓人不想看到的東西們XD