Why Your finally Runs After return
A return looks like the end of a function.
But put a finally after it:
function getValue() {
try {
return 42;
} finally {
console.log("cleanup");
}
}
console.log(getValue());
The output is:
cleanup
42
The return happened.
The function just hadn't finished leaving yet.
🔴 The Problem
It's tempting to think of return as:
return
↓
function stops immediately
But with finally, there's another step.
When JavaScript reaches:
return 42;
it evaluates 42 and prepares to return it.
Before the function actually completes, JavaScript runs the finally
block.
So the flow is roughly:
return 42
↓
remember 42
↓
run finally
↓
finish returning 42
The important part is that the return value has already been determined.
finally just gets to run before the function actually exits.
🟢 The Solution
This is exactly what makes finally useful for cleanup.
Imagine something that needs to be closed regardless of what happens:
function readFile(file) {
try {
return file.read();
} finally {
file.close();
}
}
file.read() can still determine the value returned to the caller.
But file.close() runs before the function actually leaves.
Without finally, you'd have to duplicate the cleanup:
function readFile(file) {
try {
const result = file.read();
file.close();
return result;
} catch (error) {
file.close();
throw error;
}
}
With finally, the cleanup has one place:
function readFile(file) {
try {
return file.read();
} finally {
file.close();
}
Whether try returns, throws, or finishes normally, finally gets its
chance to run.
⚡ The Dangerous Part
finally isn't limited to cleanup.
It can actually change how the function finishes.
function getValue() {
try {
return 42;
} finally {
return 100;
}
}
console.log(getValue());
The result is:
100
The original return 42 prepared the function to return 42.
Then finally executed another return.
That new return becomes the function's actual result.
The same thing can suppress an error:
function getValue() {
try {
throw new Error("Boom");
} finally {
return 42;
}
}
console.log(getValue());
Instead of throwing, the function returns:
42
The return inside finally replaced the pending exception.
That's why returning from finally is usually a bad idea.
You're no longer just cleaning up.
You're changing the outcome.
🧠 The Takeaway
Don't think of:
return value;
as:
"I am completely done now."
Think of it as:
"I know how I want to finish."
↓
finally
↓
"I actually leave now."
So this is perfectly reasonable:
try {
return result;
} finally {
cleanup();
}
But this deserves suspicion:
try {
return result;
} finally {
return somethingElse;
}
return decides the result.
finally gets the last chance to change how the function exits.


