[JS] Error Handling
keywords: exception handling
, javascript
, 錯誤處理
, 例外處理
Custom Error
- Better error handling with unknown type in TypeScript @ Youtube
- Custom Error Class @ Udemy
- Handling errors like a pro in TypeScript @ Medium
- Custom Error Types @ MDN
// error.ts
export class ApiError extends Error {
status: number;
constructor(url: string, status: number) {
super(`Request fail with ${status} on ${url}`);
if (Error.captureStackTrace) {
Error.captureStackTrace(this, ApiError);
}
this.name = 'ApiError';
this.status = status;
}
}
// fetch.ts
export async function fetchJson<T>(url: string): Promise<T> {
const resp = await fetch(url);
if (!resp.ok) {
throw new ApiError(url, resp.status);
}
const data = await resp.json();
return data;
}
const fetchProducts = async () => {
try {
const products = await fetchJson('http://example.com');
// ...
} catch (error) {
if (error instanceof ApiError) {
// handle Api Error
// we can use `error.name` and `error.status` in the Custom Error now
}
throw error;
}
};
JavaScript Error Handling Pattern
Either Pattern
Async Await try-catch hell @ Youtube Shorts
// return the function with tuple [Data?, Error?]
async function foo() {
try {
const data = await promise;
return [data, null];
} catch (error) {
return [null, error];
}
}
async function main() {
const [data, error] = await foo();
if (error) {
/* ... */
}
const [data, error] = await bar();
}
範例程式碼
// fetch.ts
import { ApiError } from './error';
type Success<T> = [T, null];
type Fail = [null, Error];
export async function fetchJson<T>(
url: string,
options?: RequestInit | undefined,
): Promise<Success<T> | Fail> {
try {
const resp = await fetch(url, options);
if (!resp.ok) {
throw new ApiError(url.toString(), resp.status);
}
const data = await resp.json();
return [data, null];
} catch (error) {
if (error instanceof Error) {
return [null, error];
}
throw error;
}
}
export async function getProducts() {
const [products, error] = await fetchJson<ApiProduct[]>(`${CMS_URL}/products`);
if (error) {
throw error;
}
return products.map(mapProduct);
}
JavaScript Error Object
try...catch...
的使用應該是用來處理「例外情況」,也就是沒有預期到會發生的事,不要把它當作 conditional flow 使用。
// Error Object
function throwError() {
try {
throw new Error('foobar');
} catch (ex) {
console.log(ex); // Error: foobar
console.log(ex.name); // Error;
console.log(ex.message); // foobar;
}
}
throwError();
本文主要內容翻譯自 Exceptional Exception Handling in JavaScript @ SitePoint
在撰寫程式的過程中發生錯誤(error)或出現例外情況(exception)是經常出現的情況,一般我們會把「錯誤」稱作「例外」,兩者可以交替著使用。
當 JavaScript 程式執行的過程中發生錯誤時,它會丟出例外狀況(throw an exception),JS 並不會繼續往下走,而是尋找有沒有任何程式碼能夠處理這些錯誤(exception handling code),如果沒有找到任何可以處理錯誤狀況的程式碼,則它會從丟出例外狀況的函式中跳出(return),就這樣重複尋找錯誤處理的程式碼、跳出,直到觸及到最外層的函式(top level function)後終止。
錯誤類型
當例外產生時,會產生一個用來代表錯誤的物件。在 JS 中內建了 7 種錯誤類型的物件:
1. Error
Error @ MDN > Standard build-in objects
Error
這個類型用來代表一般的錯誤情況,最常用在客製化例外情況。透過下面的指令可以產生一個錯誤物件的實例:
var error = new Error('error message');
console.log(error); // Error: error message
這個 Error
物件包含兩個屬性-name
和 message
:
- name: 用來說明錯誤的類型(這裡就是
Error
) - message: 提供更多關於此例外情況的描述。
2. RangeError
RangeError
的例外情況會發生在當數值落在特定的區間外時,例如,透過 toFixed()
方法時,它可以接受介於 0 ~ 20 的參數來說明要顯示到小數後第幾位,當這個參數超過這個區間時,就會拋出 RangeError
:
var pi = 3.14159;
pi.toFixed(100000); // RangeError: toFixed() digits argument must be between 0 and 100
3. ReferenceError(找不到變數:拼錯字)
當試圖存取一個不存在的變數時,會拋出 ReferenceError
,這個錯誤經常發生在拼錯字的情況。
console.log(bar); // ReferenceError: bar is not defined
4. SyntaxError(語法錯誤)
當有程式碼違反 JavaScript
的語法規則時,會拋出 SyntaxError
的錯誤。熟悉 C 或 Java 的通常是在編譯的過程中(computation process)遇到語法錯誤;但 JavaScript 是直譯式語言(interpreted language),因此當 程式碼被執行到時,才會辨認到語法錯誤。這種錯誤類型是所有例外狀況中唯一不能被修復的。
if (false) { // SyntaxError: Unexpected token (3:0)
// 缺少結束的大括號