6 ms·
This article omits getting into try-catch-finally semantics, and it's not really that mysterious when you think about it (although, yes, please don't ask people
by dvt 5y ago
This article omits getting into try-catch-finally semantics, and it's not really that mysterious when you think about it (although, yes, please don't ask people about this in an interview):
- A catch block is only executed if an exception is thrown in the try block.
- A finally block is executed always after a try(-catch) block, if an exception is thrown or not.
> As a side-effect, returning from finally clears a thrown error:
Exceptions aren't "cleared," they are "finally-d." Because that's how finally works[2]. The final block, if evaluated, always overrides the result of the previous blocks. A slightly more interesting example of try-catch weirdness is how catch blocks are one of the few constructs that create new scope (technically augment scope):
function F() {
try {
throw "error";
} catch (err) {
err = {
"hello": "world"
}
var hoisted = "foo"
console.log(err)
} finally {
return hoisted; // works, since this gets hoisted to the top of F
return err; // breaks, since err is spooky and only "scoped" in the catch block
}
}
[2] https://tc39.es/ecma262/multipage/ecmascript-language-statements-and-declarations.html#sec-try-statement https://tc39.es/ecma262/multipage/ecmascript-language-statem...
- jaffathecake 5y agoThe way I think about it: `return` and `throw` set the function's result. `catch` automatically unsets the function's result. How `finally` works can be explained from that.
- dnautics 5y agoIt's akin to how in pascal you set the return value by assigning the name of function. I think. It's been decades
- thijsvandien 5y agoOr the variable `Result` (most common nowadays), although you may also call `Exit()` with a parameter.
- jonny_eh 5y ago> `catch` automatically unsets the function's result. Do you mean finally? Even so, it doesn't automatically unset the result, it's just able to overwrite it. If a finally doesn't execute a return or throw, the previous one is used.
- jaffathecake 5y agoI don't mean finally, because it'd be wrong for the reasons you list. catch however always unsets the result. If you don't manually throw or return from a catch, then the result is an empty result (which is JavaScript is a return of undefined) finally doesn't do anything automatically, but a throw or a return changes the result.
- dahart 5y ago> Exceptions aren’t “cleared,” they are “finally-d.” Because that’s how finally works This explains exceptions thrown in the try block, but what about exceptions thrown in the catch or finally blocks? What is the reason for allowing returns in try-catch-finally blocks? Is there a good example of something that would be hard to handle otherwise? I feel like the article and examples here are all good demonstrations of why return statements should be avoided in try-catch-finally blocks.
- dvt 5y ago> What is the reason for allowing returns in try-catch-finally blocks? I think it just makes for a simpler language spec, and in this case, return is treated like any other statement; then again, there's a note here specifically about try-catch-finally blocks[1] so maybe it doesn't make for a simpler language spec :) [1] https://tc39.es/ecma262/multipage/ecmascript-language-statements-and-declarations.html#sec-return-statement https://tc39.es/ecma262/multipage/ecmascript-language-statem...
- dahart 5y agoYeah, that’s my thinking - it seems simple at first, but when you see that return and finally when used together are in conflict with their stated / expected behaviors and that the language specifies the tie breaker in a way that might be unexpected... wouldn’t it potentially be simpler both in the spec and to a developer to make return in try-catch-finally blocks a syntax error? I’m assuming there’s a reason to allow the return statements in try-catch-finally blocks, but I can’t think of a use-case. The only use for a return inside a finally block would be to cancel a return in a try or catch block, right? In what cases is that actually desirable? I can see wanting to return from catch, because something bad happened. Overriding that return in finally doesn’t make a lot of sense to me, but I’m certain the ES designers thought about it more than I did.
- jchw 5y agoI actually don’t think it’s to make the language simpler, but most likely for compatibility. First, Statement/Block are parameterized with whether or not they can contain Return statements in the spec, so it wouldn’t complicate the spec much. Second, it is exceedingly rare that JS compatibility breaks. The one recent break I am aware of is: var let = [] let[0] = 1 // Syntax error because let [ conflicts with destructuring lexical declaration.
- jchw 5y agonit: You never declare hoisted at all. Wouldn’t that assignment cause globalThis.hoisted = "foo" ? I assume you meant to declare it somehow, but it will only work if you declare it with var, which isn’t block-scoped to begin with.
- dvt 5y agoTotally correct, fixed now! I omitted the var by design since I was just trying to elucidate the difference between hoisted and err (the var keyword breaks that flow a bit).
- e12e 5y ago> return err; // breaks, since err is spooky and only "scoped" in the catch block Both err's? (the argument and the one you define)? How about: try { throw "error"; } catch (err) { var e = err; // is this different from not shadowing err? console.log(e) } finally { return e; }
- sangeeth96 5y agoOP didn't redefine `err`, just assigned a new value to the `err` variable that was available as `catch`'s parameter. Only the catch's error variable is scoped to the catch block. Any variable that's defined inside `catch` block using `var` is hoisted in the scope of the nearest function. So, the `return e` in your example works since `e` was hoisted when you defined it using `var e = err`. If you tried `return err` instead, it'd result in a `ReferenceError`.
- e12e 5y ago> OP didn't redefine `err`, just assigned a new value to the `err` variable that was available as `catch`'s parameter. How would you define "redefine" if this isn't an example of redefining? > the `return e` in your example works since `e` was hoisted when you defined it using `var e = err`. If you tried `return err` instead, it'd result in a `ReferenceError`. Ok, thank you.
- TechBro8615 5y agoI’ve been programming for ~15 years, including the last 5 years professionally in JavaScript, and I’ve yet to use a `finally`. Am I alone in this, or is it really meant for niche use cases?
- moltar 5y agoIt’s pretty common, but small surface of application. The two most common use cases I’ve seen so far: 1. Same as the article example. UI action when AJAX request completes 2. In NodeJS disconnecting from resources (db, redis) after you are done. Otherwise the script will hang and never exit.
- paulddraper 5y agoBackend code has uses for it. Like closing/releasing a database connection.
- chrisseaton 5y agoHow do you ensure that a resource you are using will be released if there is an exception somewhere in your code?