What do you think will happen if we run this code?
function test() {
try {
throw new Error("oops");
} catch (e) {
return 'catch';
} finally {
return 'finally';
}
}
console.log(test());
At first you might think:
"Okay, it throws an error, so it goes into the catch block and returns 'catch'... right?”
But not quite! Actually this code will return finally! But why? Let me explain.
How try, catch and return Work
We usually think of return as an immediate exit, but in JavaScript, that’s not exactly how it works under the hood. When the JS engine hits a return, it creates something called a Completion Record.
Think of it as a sticky note that says:
"Hey, we're done here. The reason is return and here's the value to give back"
This record is kept internally by the JavaScript engine until the function truly finishes.
The flow with finally
Here's a real-world metaphor:
You go to the market (that's your function)
You try to buy apples (try), but if they’re out of stock, you get bananas instead (catch).
As you're about to check out, your mom calls and says:
“Forget that — just buy milk!” (finally).
So no matter what you were planning to bring home (apples or bananas), you end up bringing milk.
Here's the code version:
function goToMarket() {
try {
throw new Error('No apples today!');
return 'Apples';
} catch (e) {
return 'Bananas';
} finally {
return 'Milk';
}
}
console.log(goToMarket()); // 'Milk'
Why Does This Happen?
- The
tryblock tries to return'Apples', but an error is thrown. - The
catchblock handles it and returns'Bananas'. - Before the function can return
'Bananas', thefinallyblock runs and itsreturn 'Milk'overrides everything.
So 'Milk' is the final return value.
ECMAScript Spec (in simple terms)
According to the ECMAScript spec:
- A
returnintryorcatchdoesn't immediately return — it creates a Completion Record; - The
finallyblock is always executed, regardless of what happened intryorcatch; - If the
finallyblock has its ownreturn, it overwrites any previous return;
That’s why finally becomes the actual return value.
Reference
This behavior follows the ECMAScript specification, which details how CompletionRecords are handled during function execution.
Final Thoughts
The finally block isn't just a "cleanup" section — it can take control of your function's return value if you’re not careful. Even if try or catch has a return, a return inside finally will override it.
So next time you're writing error handling, think twice before returning inside a finally block, it might silently change your function’s behavior.
Understanding this little quirk helps you avoid unexpected bugs and gives you more control over your code flow.
Console You Later!
Top comments (0)