Vì lúc callback chạy, khối try đã kết thúc từ lâu. try/catch chỉ bảo vệ những gì thực thi trên cùng call stack đồng bộ với nó.
js
try {
setTimeout(() => {
throw new Error('boom') // không ai bắt
}, 0)
} catch (err) {
console.log('không bao giờ chạy')
}setTimeout chỉ đăng ký callback rồi trả về ngay; try thoát, stack rỗng. Sau đó event loop lấy callback ra chạy trên một stack mới tinh — không còn khung catch nào phía dưới, lỗi bay lên global (window.onerror / uncaughtException).
Cách xử lý:
js
setTimeout(() => {
try {
risky()
} catch (err) {
report(err)
}
}, 0)Cùng lý do đó, callback kiểu Node (fs.readFile(path, (err, data) => ...)) truyền lỗi qua tham số err thay vì ném.
Ngược lại, await bắt được vì nó nối lại phần code sau await vào cùng ngữ cảnh hàm async, nên try/catch bao quanh await vẫn hiệu lực.