What happens when both try and finally return in JavaScript?

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 try block tries to return 'Apples', but an error is thrown. The catch block handles it and returns 'Bananas'. Before the function can return 'Bananas', the finally block runs and its return 'Milk' overrides everything. So 'Milk' is the final return value. ECMAScript Spec (in simple terms) According to the ECMAScript spec: A return in try or catch doesn't immediately return — it creates a Completion Record; The finally block is always executed, regardless of what happened in try or catch; If the finally block has its own return, 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!

Apr 7, 2025 - 04:52
 0
What happens when both try and finally return in JavaScript?

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?

  1. The try block tries to return 'Apples', but an error is thrown.
  2. The catch block handles it and returns 'Bananas'.
  3. Before the function can return 'Bananas', the finally block runs and its return 'Milk' overrides everything.

So 'Milk' is the final return value.

ECMAScript Spec (in simple terms)

According to the ECMAScript spec:

  • A return in try or catch doesn't immediately return — it creates a Completion Record;
  • The finally block is always executed, regardless of what happened in try or catch;
  • If the finally block has its own return, 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!