JavaScript - try...catch: Mastering Error Handling for Beginners

Xin chào, các bạn muốn trở thành lập trình viên! Hôm nay, chúng ta sẽ bắt đầu một hành trình thú vị vào thế giới xử lý lỗi trong JavaScript. Đừng lo lắng nếu bạn là người mới bắt đầu lập trình - tôi sẽ hướng dẫn bạn từng bước với rất nhiều ví dụ và giải thích. Cuối cùng của bài hướng dẫn này, bạn sẽ bắt lỗi như một chuyên gia!

JavaScript - try...catch

Câu lệnh JavaScript try...catch

Hãy tưởng tượng bạn đang học骑自行车. Bạn có thể ngã vài lần, nhưng điều đó không sao - bạn đứng dậy và thử lại. Trong lập trình, chúng ta sử dụng câu lệnh try...catch để làm điều tương tự. Chúng ta "thử" chạy một đoạn mã, và nếu nó "ngã" (ném ra một lỗi), chúng ta "bắt" nó và xử lý một cách nhẹ nhàng.

Hãy xem một ví dụ đơn giản:

try {
// Đoạn mã có thể gây ra lỗi
console.log(nonExistentVariable);
} catch (error) {
// Đoạn mã để xử lý lỗi
console.log("Oops! Đã xảy ra lỗi:", error.message);
}

Trong ví dụ này, chúng ta đang cố gắng ghi một biến không tồn tại. Thay vì làm chương trình của chúng ta crash, khối catch bắt lỗi và ghi một thông báo thân thiện.

Câu lệnh JavaScript try...catch...finally

Đôi khi, chúng ta muốn chạy một đoạn mã bất kể có lỗi xảy ra hay không. Đó là lúc khối finally rất hữu ích. Nó giống như nói, "Dù sao đi nữa, hãy chắc chắn làm điều này!"

try {
console.log("Hãy thử chia cho không!");
let result = 10 / 0;
console.log("Kết quả:", result);
} catch (error) {
console.log("Oh no! Đã xảy ra lỗi:", error.message);
} finally {
console.log("Điều này luôn chạy, có lỗi hay không!");
}

Trong ví dụ này, mặc dù việc chia cho không gây ra lỗi, khối finally vẫn thực thi.

Câu lệnh JavaScript Throw

Đôi khi, chúng ta muốn tạo ra lỗi của riêng mình. Chúng ta có thể làm điều này bằng cách sử dụng câu lệnh throw. Nó giống như làm trọng tài trong một trận đấu và gọi một lỗi khi bạn thấy một hành động vi phạm.

function checkAge(age) {
if (age < 0) {
throw new Error("Tuổi không thể là số âm!");
}
console.log("Tuổi hợp lệ:", age);
}

try {
checkAge(-5);
} catch (error) {
console.log("Bắt được lỗi:", error.message);
}

Ở đây, chúng ta đang ném ra một lỗi của riêng mình khi ai đó cố gắng sử dụng một tuổi âm.

N nesting Try Blocks

Giống như các búp bê嵌套, chúng ta có thể đặt các khối try...catch bên trong nhau. Điều này rất hữu ích khi chúng ta muốn xử lý lỗi ở các mức độ khác nhau trong mã của mình.

try {
try {
throw new Error("Oops!");
} catch (innerError) {
console.log("Bắt lỗi bên trong:", innerError.message);
throw innerError; // Ném lại lỗi
}
} catch (outerError) {
console.log("Bắt lỗi bên ngoài:", outerError.message);
}

Trong ví dụ này, chúng ta bắt lỗi trong khối bên trong, ghi nó, và sau đó ném lại để khối bên ngoài bắt.

Ném lại Lỗi

Đôi khi, chúng ta muốn bắt một lỗi, làm điều gì đó với nó, và sau đó chuyển nó đi để được xử lý ở nơi khác. Điều này được gọi là ném lại lỗi.

function doSomethingRisky() {
throw new Error("Nguy hiểm, Will Robinson!");
}

try {
doSomethingRisky();
} catch (error) {
console.log("Ghi lỗi:", error.message);
throw error; // Ném lại lỗi
}

Ở đây, chúng ta bắt lỗi, ghi nó, và sau đó ném lại để một phần khác của mã của chúng ta xử lý.

Khối Catch Điều kiện

Trong một số trường hợp, chúng ta có thể muốn xử lý các loại lỗi khác nhau theo cách khác nhau. Chúng ta có thể làm điều này bằng cách kiểm tra loại lỗi trong khối catch.

try {
let randomNumber = Math.random();
if (randomNumber < 0.5) {
throw new TypeError("Lỗi loại!");
} else {
throw new RangeError("Lỗi phạm vi!");
}
} catch (error) {
if (error instanceof TypeError) {
console.log("Xử lý TypeError:", error.message);
} else if (error instanceof RangeError) {
console.log("Xử lý RangeError:", error.message);
} else {
console.log("Lỗi không xác định:", error.message);
}
}

Ví dụ này cho thấy cách chúng ta có thể xử lý các loại lỗi khác nhau theo cách khác nhau.

JavaScript try...catch với phương thức setTimeout()

Khi làm việc với mã không đồng bộ, xử lý lỗi có thể rất phức tạp. Hãy xem cách xử lý lỗi trong hàm setTimeout().

try {
setTimeout(() => {
throw new Error("Lỗi async!");
}, 1000);
} catch (error) {
console.log("Điều này sẽ không bắt được lỗi!");
}

// Cách đúng:
setTimeout(() => {
try {
throw new Error("Lỗi async!");
} catch (error) {
console.log("Bắt được lỗi async:", error.message);
}
}, 1000);

Lần thử đầu tiên sẽ không hoạt động vì lỗi được ném ra sau khi khối try...catch đã kết thúc. Lần thử thứ hai bắt lỗi một cách chính xác.

Lỗi dựa trên Promise

Khi làm việc với Promises, chúng ta sử dụng .catch() để xử lý lỗi. Nó giống như khối catch, nhưng cho các thao tác không đồng bộ.

function fetchData() {
return new Promise((resolve, reject) => {
setTimeout(() => {
reject(new Error("Thất bại khi lấy dữ liệu"));
}, 1000);
});
}

fetchData()
.then(data => console.log("Dữ liệu:", data))
.catch(error => console.log("Lỗi:", error.message));

Trong ví dụ này, chúng ta mô phỏng một lần lấy dữ liệu thất bại và bắt lỗi kết quả.

Các loại Lỗi trong JavaScript

JavaScript có nhiều loại lỗi nội tại. Dưới đây là bảng tóm tắt:

Loại Lỗi Mô tả
Error Loại lỗi chung
SyntaxError Xảy ra khi có lỗi cú pháp trong mã
ReferenceError Xảy ra khi tham chiếu đến một biến không tồn tại
TypeError Xảy ra khi một giá trị không phải là loại mong đợi
RangeError Xảy ra khi một giá trị không nằm trong phạm vi mong đợi
URIError Xảy ra khi sử dụng các hàm xử lý URI toàn cục sai cách
EvalError Xảy ra khi sử dụng hàm eval() sai cách

Hiểu rõ các loại lỗi này có thể giúp bạn viết mã xử lý lỗi chính xác hơn.

Và thế là xong! Bạn đã hoàn thành một khóa học nhanh về xử lý lỗi trong JavaScript. Nhớ rằng, lỗi không phải là kẻ thù của bạn - chúng là phản hồi quý giá giúp bạn viết mã tốt hơn, mạnh mẽ hơn. Hãy tiếp tục thực hành, 保持好奇心, và chúc bạn lập trình vui vẻ!

Credits: Image by storyset