Xử lý lỗi với try/catch trong JavaScript
Lỗi là gần như không thể tránh khỏi trong lập trình nói chung và các chương trình JavaScript nói riêng. Lỗi có thể là lỗi coding do lập trình viên gây ra, lỗi do input sai và cả những lỗi không lường trước được. Thông thường trong JavaScript, nếu có lỗi, một vài điều có thể xảy ra:
- Trình duyệt sẽ lặng lẽ [silently] tự động báo lỗi của mã JavaScript xung quanh nơi xảy ra lỗi không được thực thi. Chúng ta có thể nhấn phím F12 để xem những thông báo lỗi này.
- Hoặc trình duyệt sẽ tạo ra các cửa sổ pop up gây phiền nhiễu cho người dùng để thông báo rằng đã xảy ra lỗi.
Nhưng với những kiểu thông báo trên đều không phải là tùy chọn lý tưởng, là một nhà phát triển JavaScript, chúng ta cần dự đoán các lỗi và xử lý chúng một cách hiệu quả. Điều này cuối cùng sẽ giúp bạn tạo ra các chương trình mạnh mẽ, đáng tin cậy và hiệu quả. Một cách đơn giản để xử lý lỗi là thông qua câu lệnh trycatchfinally
- Khối lệnh try/catch/finally
- Đặc điểm của khối lệnh finally
- Câu lệnh throw
- Đối tượng error
- Khối lệnh try/catch/finally có thể được lồng [nested] vào nhau
1. Khối lệnh try/catch/finally
Trong block try cho dù chứa một hoặc nhiều câu lệnh cũng đều phải đặt trong cặp dấu ngoặc nhọn {}. Theo sau đó bắt buộc phải có ít nhất block lệnh của catch hoặc block lệnh của finally. Do đó, sẽ có 3 dạng [forms] của câu lệnh try:
- trycatch
- tryfinally
- trycatchfinally
Cú pháp:
try { Các lệnh thực thi } catch[err] { Các lệnh xử lý lỗi [handle errors] } finally { Các lệnh ở đây sẽ luôn được thực hiện bất kể kết quả của try / catch }Cách thức hoạt động:
- Đầu tiên sẽ thực thi các lệnh của khối try.
- Sau khi thực thi các lệnh của khối try. Nếu không có lỗi thì khối catch sẽ được bỏ qua, ngược lại nếu có lỗi thì các lệnh xử lý lỗi ở khối catch sẽ được thực thi.
- Dù có lỗi hay không có lỗi thì các lệnh của khối finally luôn được thực thi.
Trường hợp chương trình có lỗi: nếu có xử lý lỗi với try/catch thì chương trình vẫn được thực hiện đến cuối cùng. Nếu không có try/catch thì chương trình sẽ stop ngay tại dòng gây ra lỗi.
Ví dụ sau minh họa lỗi gọi hàm chưa được định nghĩa:
2. Đặc điểm của khối lệnh finally
Ngoài đặc điểm: sau khi thực thi các lệnh ở khối try dù có lỗi hay không có lỗi thì các lệnh của khối finally luôn được thực thi [như ví dụ ], thì finally còn một đặc điểm khác khá đặc biệt mà chúng ta sẽ xét ngay sau đây.
Các bạn có thể thấy ví dụ và cho ra một kết quả như nhau, vậy chúng ta cần finally để làm gì? Giả sử bạn viết một hàm để trả về kết quả tính toán nào đó, nhưng sau lệnh return bạn còn muốn thực hiện xử lý abc, xyz, nữa trước khi kết thúc hàm, chẳng hạn như close file hay dọn dẹp rác hủy các object đã khởi tạo,
Khi đó, nếu bạn đặt các xử lý abc, xyz, này vào trong khối finally, thì dù có đặt sau lệnh return, các xử lý đó vẫn được thực thi.
Để minh họa cho điều này, chúng ta sẽ quay lại với hai ví dụ và có sử dụng lệnh return:
3. Câu lệnh throw
Câu lệnh throw cho phép bạn tạo ra một thông báo lỗi riêng tùy ý [custom error].
Về mặt kỹ thuật bạn có thể ném ra một ngoại lệ [exception] [throw ra một error]. Ngoại lệ có thể là một chuỗi JavaScript, một số, một boolean hoặc một đối tượng:
Nếu bạn sử dụng throw cùng với try và catch, bạn có thể kiểm soát luồng chương trình và tạo các thông báo lỗi tùy chỉnh:
4. Đối tượng error
JavaScript có một error object được xây dựng để cung cấp thông tin lỗi khi xảy ra lỗi.
Error object có hai thuộc tính hữu ích là: name và message.
Property | Description |
name | Thiết lập [set] hoặc return tên của lỗi [error name]. |
message | Thiết lập [set] hoặc return một chuỗi thông báo lỗi [error message]. |
Thuộc tính error name có 6 giá trị khác nhau có thể được return:
Error Name | Description |
EvalError | Đã xảy ra lỗi trong hàm eval[]. |
RangeError | Đã xảy ra lỗi một số ngoài phạm vi. |
ReferenceError | Đã xảy ra lỗi tham chiếu không hợp lệ. |
SyntaxError | Đã xảy ra lỗi cú pháp. |
TypeError | Đã xảy ra lỗi type. |
URIError | Đã xảy ra lỗi trong encodeURI[]. |
EvalError chỉ ra một lỗi trong hàm eval[].
Các phiên bản JavaScript mới không ném [throw] ra bất kỳ EvalError nào. Sử dụng SyntaxError để thay thế.
Một lỗi RangeError được ném [throw] ra nếu bạn sử dụng một số nằm ngoài phạm vi giá trị hợp lệ.
Ví dụ: Bạn không thể set một số có tổng các chữ số là 500.
Xem thêm về hàm toPrecision[] ở bài viết: Thuộc tính và phương thức của đối tượng JavaScript Number.
Một lỗi ReferenceError được ném [throw] ra nếu bạn sử dụng [tham chiếu] một biến chưa được khai báo:
Một lỗi SyntaxError được ném [throw] ra nếu bạn cố gắng evaluate code với một cú pháp lỗi:
Một lỗi TypeError được ném [throw] ra nếu bạn sử dụng giá trị nằm ngoài phạm vi của type được mong đợi:
Một lỗi URIError được ném [throw] ra nếu bạn sử dụng các ký tự không hợp lệ trong hàm URI:
Trường hợp các xử lý ở khối try có thể phát sinh lỗi mà bạn dự đoán được thì trong khối catch có thể sử dụng câu lệnh if xử lý riêng cho từng lỗi.
5. Khối lệnh try/catch/finally có thể được lồng [nested] vào nhau
Đầu tiên, hãy xem điều gì xảy ra với ví dụ bên dưới:
Bây giờ, chúng ta sẽ thêm một khối catch vào bên trong khối try-block inner:
Và bây giờ, sẽ ném lại lỗi throw [re-throw error] ở khối catch vừa thêm vào:
Như vậy, qua 3 ví dụ , , chúng ta thấy:
- Ngoại lệ [exception] chỉ được bắt [catch] một lần bởi khối catch bao quanh gần nhất trừ khi nó được ném lại [re-throw].
- Tất nhiên, bất kỳ ngoại lệ mới [new exception] nào được raised lên bên trong khối inner [vì code trong khối catch có thể throw ra một vài lỗi j đó] sẽ bị chặn bởi khối outer.
Trường hợp return từ finally block
Ở ví dụ , chúng ta sẽ thêm lệnh return; vào finally block thì kết quả sẽ thế nào?
Nếu finally block trả về một giá trị, thì giá trị này sẽ trở thành giá trị trả về của toàn bộ quá trình try-catch-finally, bất kể có bất kỳ câu lệnh return nào trong khối try và catch. Điều này bao gồm các exception được ném vào bên trong khối catch.