Ngoại lệ

Câu lệnh quản lý lỗi

Bạn có thể ném ngoại lệ (Exception) bằng cách sử dụng câu lệnh throw và xử lý ngoại lệ bằng cách sử dụng câu lệnh try ... catch.

Chúng ta có thể ném bất cứ kiểu Exception nào.
Ví dụ

1
2
3
4
5
6
7
8
throw 'Error2';   // kiểu String
throw 42;         // kiểu Number type
throw true;       // kiểu Boolean
throw {           // kiểu Object
  toString() { 
    return "I'm an object!";
  }
};

Thường thì chúng ta sẽ tạo một đối tượng riêng để ném ngoại lệ.

1
2
3
4
5
6
7
8
9
// Tạo một đối tượng kiểu UserException
function UserException(message) {
  this.message = message;
  this.name = 'UserException';
  this.toString = function() {
    return `${this.name}: "${this.message}"`;
  }
}
throw new UserException('Value too high');

Câu lệnh try ... catch

Câu lệnh try ... catch kiểm tra một khối các câu lệnh và nếu một ngoại lệ được ném ra, câu lệnh catch sẽ bắt nó. Nếu ngoại lệ được ném ra trong khối try các câu lệnh sau nó không được thực thi mà thực thi ngay khối catch.

Ví dụ:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
function getMonthName(mo) {
  mo = mo - 1; // Thay đổi index của array (1 = Jan, 12 = Dec)
  let months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul',
                'Aug', 'Sep', 'Oct', 'Nov', 'Dec'];
  if (months[mo]) {
    return months[mo];
  } else {
    throw 'InvalidMonthNo'; // ném ra một ngoại lệ
  }
}

try { // câu lệnh try
  monthName = getMonthName(13); // Hàm này có thể ném ra lỗi
}
catch (e) {
  // xử lý khi có lỗi
  console.error(e);
}

Để in lỗi ra console chúng ta nên sử dụng console.error() thay vì console.log()

Khối lệnh finally

Khối finally chứa các câu lệnh sẽ được thực thi sau khi các khối thử và bắt thực thi. Cần lưu ý khối finally sẽ thực thi cho dù có ném ngoại lệ hay không.

Ví dụ sau đây mở một file và sau đó thực thi các câu lệnh sử dụng file. (Nodejs cho phép truy cập các file.) Sử dụng finally ở đây đảm bảo file không bao giờ bị mở, ngay cả khi xảy ra lỗi.

1
2
3
4
5
6
7
8
openMyFile();
try {
  writeMyFile(theData); // Câu lệnh này có thể ném ngoại lệ
} catch(e) {  
  handleError(e); // Nếu ngoại lệ xảy ra thì xử lý
} finally {
  closeMyFile(); // Đóng lại các tài nguyên đã sử dụng
}

Nếu khối finally return một giá trị, giá trị này sẽ trở thành giá trị trả về của toàn bộ khối try ... catch ... finally, bất kể câu lệnh return nào khác trong khối trycatch:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
function f() {
  try {
    console.log(0);
    throw 'bogus';  // ném ra một ngoại lệ
  } catch(e) {
    console.log(1);
    return true;    // câu lệnh return này bị chặn lại
                    // nhường chỗ cho khối lệnh finally  
    console.log(2); // không chạy câu lệnh này
  } finally {
    console.log(3);
    return false;   // chạy đè câu lệnh `return` trước
    console.log(4); // không chạy câu lệnh này
  }
  console.log(5);   // không chạy câu lệnh này
}
console.log(f()); // 0, 1, 3, false

Ghi đè các giá trị trả về của khối finally cũng áp dụng cho các ngoại lệ được ném hoặc ném lại bên trong khối catch.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
function f() {
  try {
    throw 'bogus';
  } catch(e) {
    console.log('Bắt bên trong "bogus"');
    throw e; // Câu lệnh này bị chặn lại tới khi finally thực thi xong
  } finally {
    return false; // ghi đè câu lệnh throw ở trước
                  // có nghĩa hàm này chỉ retur false, không ném
                  // ngoại lệ
  }
}

try {
  console.log(f()); // thực thi hàm f(), trả về false
} catch(e) {
  // Đoạn code này không chạy vì 
  // không có ngoại lệ nào được ném trong f()
  console.log('Bắt bên ngoài "bogus"');
}

// ->
// Bắt bên trong "bogus"
// false