Computer >> Hướng Dẫn Máy Tính >  >> Lập Trình >> JavaScript

Khối finally trong JavaScript - Luôn thực thi sau try/catch

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 ý:

  • finally không nhận tham số error
  • Có thể return trong finally (ghi đè return của try/catch) - **không khuyên dùng**
  • throw trong finally sẽ ghi đè error từ catch
  • Optional Catch Binding (ES2019): catch { } không cần tham số