Khối finally luôn được thực thi **sau khi try và catch hoàn tất**, bất kể có lỗi hay không. Dùng cho **cleanup** (đóng file, hủy timer, tắt loading, trả resource).
Luồng thực thi:
try {
// Code rủi ro
// Nếu OK: bỏ qua catch → chạy finally → tiếp tục
// Nếu lỗi: nhảy catch → chạy catch → chạy finally → tiếp tục (hoặc throw lại)
} catch (err) {
// Xử lý lỗi
} finally {
// LUÔN CHẠY (trừ process.exit, crash, infinite loop)
}
Ví dụ minh họa
<!DOCTYPE html>
<html lang="vi">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Finally JS</title>
<style>
body { font-family: "Segoe UI", Tahoma, Geneva, Verdana, sans-serif; padding: 20px; }
.sample, .result { font-size: 18px; font-weight: 500; margin: 10px 0; }
button { padding: 8px 16px; cursor: pointer; }
</style>
</head>
<body>
<h1>Finally trong JavaScript</h1>
<div class="sample" id="sample"></div>
<div class="result" id="result"></div>
<button class="Btn" id="btn">NHẤN VÀO ĐÂY</button>
<h3> Nhấn nút để gây lỗi và xem finally chạy </h3>
<script>
const sampleEle = document.querySelector("#sample");
const resEle = document.querySelector("#result");
document.querySelector("#btn").addEventListener("click", () => {
try {
// Gây ReferenceError: truy cập 'a' trước khai báo (TDZ)
resEle.innerHTML = a;
} catch (err) {
resEle.innerHTML = "❌ Error = " + err.message + "
";
resEle.innerHTML += "Error name = " + err.name + "
";
} finally {
// Luôn chạy
sampleEle.innerHTML = "✅ Code trong finally đã thực thi!";
}
let a = 44; // Khai báo sau (TDZ)
});
</script>
</body>
</html>
Kết quả
❌ Error = Cannot access 'a' before initialization Error name = ReferenceError ✅ Code trong finally đã thực thi!
Các trường hợp finally KHÔNG chạy:
process.exit()(Node.js)- Browser crash / kill tab / force close
- Vòng lặp vô tận trong try/catch
window.close()(một số trường hợp)- Stack overflow
Ứng dụng thực tế:
// 1. Resource cleanup (File, DB, Network)
let connection;
try {
connection = await connectDB();
const data = await connection.query("SELECT * FROM users");
return data;
} catch (err) {
logger.error(err);
throw err;
} finally {
if (connection) await connection.close(); // Luôn đóng
}
// 2. Loading state
setLoading(true);
try {
const data = await fetchData();
render(data);
} catch (err) {
showError(err);
} finally {
setLoading(false); // Luôn tắt spinner
}
// 3. Timer/Interval cleanup
const timer = setInterval(() => {}, 1000);
try {
// ... operation
} finally {
clearInterval(timer);
}
// 4. Lock/Mutex release
const lock = await acquireLock();
try {
await criticalSection();
} finally {
await releaseLock(lock);
}
// 5. Return trong try - finally VẪN chạy trước khi return
function test() {
try {
return 1;
} finally {
console.log("Finally runs before return!");
}
}
test(); // Log "Finally runs..." → return 1
finally với async/await:
async function fetchData() {
showSpinner();
try {
const response = await fetch('/api/data');
return await response.json();
} catch (err) {
showError(err);
throw err;
} finally {
hideSpinner(); // Luôn chạy
}
}
Lưu ý:
finallykhông nhận tham số error- Có thể
returntrong finally (ghi đè return của try/catch) - **không khuyên dùng** throwtrong finally sẽ ghi đè error từ catch- Optional Catch Binding (ES2019):
catch { }không cần tham số