Xử lý lỗi
Xử lý lỗi và try/catch
throw, try/catch/finally của Node.js so với error value (Go), Result (Rust), throws (Swift), exception (Java).
JavaScript cho phép throw bất cứ thứ gì, ở bất cứ đâu, và người gọi không hề biết hàm có thể ném lỗi. Các ngôn ngữ dưới đây chia thành hai trường phái:
- Lỗi là giá trị trả về: Go, Rust. Người gọi buộc phải xử lý.
- Exception: Swift, Java. Giống JavaScript nhưng chặt hơn về kiểu.
Định nghĩa, ném và bắt lỗi
class NotFoundError extends Error {
constructor(id) {
super(`user ${id} not found`);
this.name = "NotFoundError";
}
}
function findUser(id) {
if (id !== 1) throw new NotFoundError(id);
return { id, name: "neko" };
}
try {
const user = findUser(2);
console.log(user.name);
} catch (err) {
if (err instanceof NotFoundError) console.error(err.message);
else throw err;
} finally {
console.log("done");
}package main
import (
"errors"
"fmt"
)
var ErrNotFound = errors.New("not found") // sentinel error
type User struct {
ID int
Name string
}
// Lỗi là giá trị trả về cuối cùng.
func findUser(id int) (User, error) {
if id != 1 {
return User{}, fmt.Errorf("user %d: %w", id, ErrNotFound) // %w: bọc lỗi gốc
}
return User{ID: id, Name: "neko"}, nil
}
func main() {
defer fmt.Println("done") // gần giống finally
user, err := findUser(2)
if errors.Is(err, ErrNotFound) { // ~ instanceof, xuyên qua các lớp bọc
fmt.Println(err) // user 2: not found
return
}
if err != nil {
panic(err)
}
fmt.Println(user.Name)
}use std::fmt;
#[derive(Debug)]
enum UserError {
NotFound(u32),
}
impl fmt::Display for UserError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
UserError::NotFound(id) => write!(f, "user {id} not found"),
}
}
}
impl std::error::Error for UserError {}
struct User {
name: String,
}
fn find_user(id: u32) -> Result<User, UserError> {
if id != 1 {
return Err(UserError::NotFound(id));
}
Ok(User { name: "neko".into() })
}
fn main() {
match find_user(2) {
Ok(user) => println!("{}", user.name),
Err(err) => eprintln!("{err}"), // compiler bắt bạn xử lý cả hai nhánh
}
println!("done");
}enum UserError: Error {
case notFound(id: Int)
}
struct User {
let id: Int
let name: String
}
// `throws` bắt buộc khai báo; người gọi phải dùng `try`.
func findUser(id: Int) throws -> User {
guard id == 1 else { throw UserError.notFound(id: id) }
return User(id: id, name: "neko")
}
do {
let user = try findUser(id: 2)
print(user.name)
} catch UserError.notFound(let id) {
print("user \(id) not found")
} catch {
print("unexpected: \(error)") // `error` có sẵn trong catch cuối
}
print("done") // Swift không có finally, dùng `defer` trong hàmpublic class Main {
// Checked exception: compiler bắt khai báo `throws` hoặc bắt lỗi.
static class NotFoundException extends Exception {
NotFoundException(int id) {
super("user " + id + " not found");
}
}
record User(int id, String name) {}
static User findUser(int id) throws NotFoundException {
if (id != 1) throw new NotFoundException(id);
return new User(id, "neko");
}
public static void main(String[] args) {
try {
User user = findUser(2);
System.out.println(user.name());
} catch (NotFoundException e) {
System.err.println(e.getMessage());
} finally {
System.out.println("done");
}
}
}Ném lỗi tiếp lên trên (rethrow)
Trong JavaScript, lỗi không bắt sẽ tự nổi lên người gọi. Go và Rust bắt bạn viết rõ điều đó:
function greet(id) {
const user = findUser(id); // lỗi tự nổi lên
return `hi ${user.name}`;
}func greet(id int) (string, error) {
user, err := findUser(id)
if err != nil {
return "", fmt.Errorf("greet: %w", err) // trả lỗi lên, kèm ngữ cảnh
}
return "hi " + user.Name, nil
}fn greet(id: u32) -> Result<String, UserError> {
let user = find_user(id)?; // `?`: có lỗi thì return Err ngay
Ok(format!("hi {}", user.name))
}func greet(id: Int) throws -> String {
let user = try findUser(id: id) // `try` đánh dấu chỗ có thể ném
return "hi \(user.name)"
}static String greet(int id) throws NotFoundException { // phải khai báo tiếp
User user = findUser(id);
return "hi " + user.name();
}Lấy giá trị mặc định khi lỗi
let name;
try {
name = findUser(2).name;
} catch {
name = "guest";
}name := "guest"
if user, err := findUser(2); err == nil {
name = user.Name
}let name = find_user(2)
.map(|user| user.name)
.unwrap_or_else(|_| "guest".to_string());let name = (try? findUser(id: 2))?.name ?? "guest"String name;
try {
name = findUser(2).name();
} catch (NotFoundException e) {
name = "guest";
}Tham khảo: github.com/miguelmota/golang-for-nodejs-developers#errors