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.
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.
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.
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.
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.
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.
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.');
}
}
