Cách đây khá lâu tôi có một bài viết nói về Một số phương pháp xử lý lỗi (error handling) trong Node.js, nội dung trong bài xoay quanh việc làm thế nào để bắt được lỗi và xử lý chúng một cách dễ dàng. Trong bài viết này, tạm thời bỏ qua những phương pháp đó, chúng ta hãy đi sâu vào phân tích việc "đẩy" ra lỗi và làm sao để "bắt" được chúng.
Tôi cá là có nhiều người, trong đó có cả tôi từng đẩy ra một lỗi trông giống như thế này:
function login(username, password) {
// code
if (password != hash_password) {
throw new Error("Password is incorrect");
}
// code
}
Sau đó "bắt" lỗi:
try {
login("admin", "123456");
} catch(e) {
console.log(e.message);
// logic handle
}
Chẳng có gì nhiều để bàn đến cách xử lý lỗi ở trên cả, khi "đẩy" ra một Error
, nó sẽ mang theo rất nhiều thông tin hữu ích gây ra lỗi như vị trí (stack trace) và thông tin lỗi (message). Vì thế cho nên e
ở trong catch(e)
giúp ta dễ dàng truy ra được nguồn gốc lỗi để từ đó xử lý sao cho hợp lý.
Tôi từng gặp trường hợp đẩy ra một lỗi không đúng cách, thay vì new Error
, lại đẩy ra một chuỗi hay một đối tượng. Giống như là:
throw 'Password is incorrect';
Với cách này, chúng ta vẫn có thể try catch
. Tuy nhiên, e
lúc này chỉ đơn giản là một chuỗi hoặc một đối tượng mà bạn đã đẩy ra trước đó, nó không chứa những thông tin quan trọng như stack trace và message như của Error
. Chính vì thế, bất cứ khi nào đẩy ra một lỗi, hãy biến nó thành một instance của Error
.
Error
chỉ nhận vào tham số là một chuỗi, nó cũng chính là nội dung củae.message
. Nếu ta cố tình truyền vào nhiều hơn một tham số hoặc tham số là một thứ gì đó khác string
như object
thì sẽ gây ra một hiện tượng giống như dưới đây:
throw new Error({ "name": "2coffee" });
VM400:1 Uncaught Error: [object Object]
at <anonymous>:1:7
Sở dĩ tôi nêu ra vấn đề này vì có nhiều trường hợp, chúng ta cần thêm nhiều thông tin được vào trong một lỗi để tiện cho việc xử lý logic nào đó. Giả dụ như bên cạnh message
, tôi cần thêm uuid
là id của bản ghi gây ra lỗi, detail
chứa một mô tả chi tiết hơn, code
để chỉ định mã lỗi được định nghĩa trong hệ thống... thì với Error
, hoàn toàn là không được.
Vậy có cách nào làm được điều này không?
Error
là một đối tượng bị "đẩy" ra khi xảy ra lỗi trong thời gian chạy (runtime error). Error
cũng có thể được sử dụng để làm đối tượng cơ sở cho các lỗi do người dùng xác định. Hay nói một cách dân dã là có thể tạo ra một Class kế thừa Error
để tạo ra một đối tượng lỗi cho riêng mình.
JavaScript cung cấp một số đối tượng lỗi "tùy chỉnh" khác dựa trên Error
mà có thể bạn đã từng gặp rất nhiều rồi như:
Để xem danh sách đầy đủ Error types, bạn đọc tham khảo tại Error types - Mozilla.
Điểm chung là chúng dựa trên Error
nên mang đầy đủ thuộc tính quan trọng của Error
. Ngoài ra, các lỗi này còn mang một sự minh bạch trong mã của bạn. Ví dụ khi đẩy ra một lỗi RangeError
chúng ta có thể biết lỗi là do một giá trị nào đó nằm ngoài giá trị cho phép, thay vì throw new Error('The argument must be between 1 and 10')
một cách chung chung.
Để phân biệt các đối tượng lỗi, chỉ cần thông qua các câu lệnh điều kiện như if...else
, switch...case
...
if (err instanceof RangeError) {
// handle RangeError
} else if (err instanceof ReferenceError) {
// handle TypeError
}
...
Các đối tượng lỗi tùy chỉnh này cũng tương tự như Error
, chỉ khác mỗi name
. Do đó nếu cần một lỗi "khác biệt" hơn, hãy chuyển sang phần tiếp theo.
Chúng ta có thể tự xác định các loại lỗi của riêng mình bằng cách kế thừa từ Error
. Sau đó, đẩy ra lỗi bằng cách throw MyError
và sử dụng instanceof MyError
để kiểm tra loại lỗi trong catch
. Điều này dẫn đến các đoạn mã xử lý lỗi sạch sẽ và nhất quán hơn.
Cú pháp đơn giản nhất để tạo ra một MyError
lỗi tùy chỉnh:
class MyError extends Error {
constructor(message) {
super(message);
this.name = this.constructor.name;
}
}
MyError
lúc này được gán name
bằng constructor.name
cũng chính là cái tên MyError
của class. Tuy vậy, nó chưa có nhiều khác biệt gì so với Error
thông thường, chỉ khác mỗi name
. Chúng ta cần thêm một vài thuộc tính như code
là mã lỗi và statusCode
để định nghĩa cho HTTP response status codes.
class MyError extends Error {
code;
statusCode;
constructor(message, code, statusCode) {
super(message);
this.name = this.constructor.name;
this.code = code;
this.statusCode = statusCode;
}
}
Để đẩy ra lỗi, sử dụng cú pháp throw MyError
:
try {
throw new MyError("My error message", 123, 404);
} catch (err) {
console.log(err.name, err.message, err.code, err.statusCode);
// MyError My error message 123 404
}
Dựa vào tính năng này, có thể tạo ra nhiều lỗi riêng cho mình để phục vụ cho mục đích xử lý. Ví dụ, tạo ra class ApplicationError
, DatabaseError
, ValidateError
... có các tính năng tương tương với lỗi hệ thống, lỗi cơ sở dữ liệu, lỗi xác thực dữ liệu... để từ đó ẩn hoặc hiện thông báo lỗi cho người dùng.
Error
là một đối tượng để xử lý lỗi trong chương trình viết bằng JavaScript. Cách tốt nhất để đẩy ra một lỗi là đẩy ra một Error
thay vì chuỗi hoặc một đối tượng khác. JavaScript định nghĩa sẵn một vài "kiểu" lỗi dựa trên Error
như ReferenceError
, SyntaxError
, RangeError
... Ngoài ra, có thể tự tạo kiểu lỗi cho riêng mình bằng cách kế thừa từ Error
.
Tài liệu tham khảo:
Tôi & khao khát "chơi chữ"
Bạn đã thử viết? Và rồi thất bại hoặc chưa ưng ý? Tại 2coffee.dev chúng tôi đã có quãng thời gian chật vật với công việc viết. Đừng nản chí, vì giờ đây chúng tôi đã có cách giúp bạn. Hãy bấm vào để trở thành hội viên ngay!
Đăng ký nhận thông báo bài viết mới
Bình luận (0)