The .finally() method is a critical part of the Promise lifecycle, introduced in ES2018. It allows you to schedule a function to be executed regardless of whether the Promise is fulfilled (successful) or rejected (failed). This makes it the ideal location for “cleanup” logic that must run no matter what the outcome of the asynchronous operation is.


1. Core Logic and Syntax

The .finally() method is appended to the end of a promise chain. It does not receive any arguments because its purpose is to perform actions that are independent of the promise’s specific result.

JavaScript

promise
  .then((value) => {
    // Logic for success
  })
  .catch((error) => {
    // Logic for failure
  })
  .finally(() => {
    // Cleanup logic that runs in both cases
  });

2. Practical Use Cases

A. Loading Indicators in UI

One of the most common uses for .finally() is managing “loading” states in web applications. You want to hide the loading spinner whether the data fetch succeeded or crashed.

JavaScript

showLoadingSpinner();

fetch('https://api.example.com/data')
  .then(response => response.json())
  .then(data => renderData(data))
  .catch(err => showErrorPage(err))
  .finally(() => {
    // This ensures the spinner disappears even if the network fails
    hideLoadingSpinner();
  });

B. Closing Database or File Connections

If you open a resource (like a database connection or a file stream), you must close it to prevent memory leaks, regardless of whether your operations on that resource were successful.

JavaScript

db.connect()
  .then(connection => {
    return connection.query('SELECT * FROM users');
  })
  .then(rows => console.log(rows))
  .catch(err => console.error(err))
  .finally(() => {
    db.close(); // The connection is safely closed every time
  });

3. Advanced Behavior: The “Pass-Through”

Unlike .then(), which can “swallow” a value or transform it for the next step in the chain, .finally() preserves the state of the promise chain.

JavaScript

Promise.resolve('Success')
  .finally(() => {
    console.log('Cleaning up...');
    return 'Something else'; // This return is IGNORED
  })
  .then((value) => {
    console.log(value); // Still logs "Success"
  });

4. Cleaning Up Resources (Node.js/Database)

In back-end environments, .finally() is indispensable for closing database connections or file descriptors to prevent memory leaks or locked resources.

JavaScript

const client = await database.connect();

client.query('SELECT * FROM users')
  .then(res => process(res))
  .catch(err => log(err))
  .finally(() => {
    client.release(); // Ensure connection is returned to the pool
    console.log('Database connection closed.');
  });

5. Implementation with Async/Await

In modern JavaScript, the finally keyword is used directly within a try...catch block to achieve the same result as the .finally() method.

JavaScript

async function loadData() {
  try {
    const response = await fetch('/api/resource');
    const data = await response.json();
    return data;
  } catch (error) {
    handleError(error);
  } finally {
    console.log('Operation attempt finished.');
  }
}

Categorized in:

Javascript,