Xử lý lỗi với Async/Await trong JavaScript — không chỉ có try/catch
Mở đầu — chuyện tới muộn mà code chạy sai
Ảnh: Lukas Blazek — Pexels
Hồi mới tập tành async/await, mình cứ nghĩ xử lý lỗi chỉ đơn giản là quăng mọi thứ vô try/catch là xong. Ai dè càng code nhiều càng thấy pattern ai cũng biết đó lại không phải lúc nào cũng là giải pháp tốt nhất.
Mình gặp hoài cái cảnh: một cái API call lỗi → cả app crash, hoặc tệ hơn là error bị silent nuốt mất không ai hay. Rồi mới ngồi research và thử nghiệm mấy pattern khác nhau. Hôm nay mình ghi lại để sau này còn nhớ, may ra có ai đọc cũng thấy hữu ích.
1. Pattern cơ bản — try/catch
Cái này ai cũng biết, mình khỏi giải thích nhiều. Nhưng cái mình muốn nói là: đừng catch rồi console.log rồi thôi. Catch mà không xử lý gì thì khác nào không catch:
// Đừng làm vầy
try {
const data = await fetchAPI()
} catch (err) {
console.error(err) // xong rồi thôi? data undefined ở dưới kìa
}
// Nên làm vầy
try {
const data = await fetchAPI()
renderData(data)
} catch (err) {
showErrorFallback(err)
logToService(err) // log có ích
}
2. Pattern .catch() trên promise — gọn hơn, dễ đọc hơn
Có một pattern mình thấy ít người dùng nhưng rất hiệu quả, nhất là khi bạn có fallback value:
const data = await fetchAPI().catch(() => defaultData)
// Nếu fetch fail, data vẫn có giá trị mặc định — app không chết
Cái hay là code chạy tiếp, UI không bị trắng xoá. Mình áp dụng pattern này cho mấy cái widget không quan trọng — ví dụ fetching stock ticker hay weather banner.
3. Pattern Top-level error boundary cho async functions
Khi mình viết hàm async mà muốn chắc chắn không có unhandled rejection, mình hay dùng wrapper nhỏ:
function to(promise) {
return promise.then(data => [null, data])
.catch(err => [err, null])
}
const [err, data] = await to(fetchAPI())
if (err) {
// xử lý lỗi ở đây, không lo crash
}
Pattern Go-style này mình học từ một bài blog tiếng Anh, thấy rất sạch và dễ maintain. Đặc biệt hữu ích khi có nhiều async calls nối tiếp nhau.
Ảnh: Brett Jordan — Pexels
Lời kết — không có silver bullet
Tuỳ context mà chọn pattern phù hợp. Mình thấy nhiều bạn cứng nhắc quá, cái gì cũng try/catch dù chỉ fetch một cái không quan trọng. Rồi có bạn lại lạm dụng .catch() trên promise chain mà quên mất cần fallback UI cho user.
Nói chung, hiểu được nhiều pattern rồi thì tự biết khi nào xài cái nào. Quan trọng nhất là: đừng để error bị nuốt mất, và đừng để app crash vì một cái API call lẻ.
Có bạn nào có pattern hay ho hơn không? Chia sẻ mình học hỏi với nha!
📋 Phụ lục thuật ngữ
- try/catch — Khối lệnh JavaScript để bắt và xử lý exception đồng bộ hoặc async
- .catch() — Method trên Promise object để handle rejection
- Unhandled rejection — Promise bị reject mà không có .catch() hoặc try/catch, có thể crash app trong Node.js
- Fallback value — Giá trị mặc định dùng khi request thất bại