You'd be surprised how many people don't know about 'finally' at all. Given how many of JS APIs are async, there hasn't been much call for 'finally', so folks haven't encountered it. I think they're useful again with async functions, which is why I documented that. You might be _very smart_ and know that stuff already, but the article wasn't written specifically for you.
Having worked at the BBC, I'm a fan of the Reithian principles; Educate, entertain, and inform. Yes, there's a central point to the article, but I like using that as a starting point to look at related things, like async functions, and the promise stuff.
"Please never quiz folks about this in a job interview."
I have seen these types of 'well if they get this it's a good sign' type of questions. I guess if you really want that you could ask "What is a little known feature in a language you know well?"
A better question would be, especially for JavaScript; “what are the bad parts about JavaScript?”. A person with actual real life experience will at least be able to list a couple.
As always, I think discussion really helps back it up.
But I'd expect someone who has had experience with some language/library/framework will have run into problems with it, and so will be able to discuss some problem which hints at that experience.
For most things, yes. But this is JavaScript, the language where intransitive equality, implicit object-to-primitive casting and mutable global objects must be used in all slight-above-trivial code. Where `typeof` is useless and wrong, but nonetheless necessary; where there are seven different ways to parse a number, with six different semantics… there's a reason that “JavaScript: The Good Parts” is such a short book. https://blog.klipse.tech/assets/js_good_parts.jpg
Somebody who knows the good parts of JavaScript is a JavaScript expert. You want somebody who can write good JavaScript, and using the bad parts well has nothing on using the good parts adequately.
I disagree, knowing the good parts about JavaScript isn’t difficult. What’s more difficult is being able to explain the bad parts about your main language. And JavaScript has quite a few, if you’re not able to highlight those, I wouldn’t consider you an expert at all.
By knowing all the bad parts, you’ll be able to write much better code, because you know what to look out for.
"Tell me about the weirdest bug you had to deal with" is a good way to cover this, and more. But really, you find out more about communication skills with this question than anything else.
C# does not allow returns from a `finally` block, which has annoyed me at times. However, seeing the consequences of allowing the behavior realized in this article, I am happy for the rule.
> C# does not allow returns from a `finally` block
I think C# does not allow any flow control in finally blocks, so no continue or break either (well for continue and break they're allowed if the statement they "refer to" is also in the finally block, but flow control can't go through or escape from the block).
Essentially in C# a finally block can only be escaped from by falling off of the block's end.
Looks like a good minimum example to show that the intuitive natural language semantics of try and finally don't work with statements like continue that change control flow.
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
}
}
> `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.
I 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.
> 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.
> 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 :)
Yeah, 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.
I 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.
> why return statements should be avoided in try-catch-finally blocks
When I was a wee lad, learning basic and C, my father tried to impress upon me that a function should never have more than one `return` statement.
Back in those days, all these languages had `goto` statements, and we were told never to use them, because they were bad. But goto isn't bad because "goto" is a bad word - it makes your life unnecessarily complicated. A goto makes it very hard to reason about code, because you never know how you got to a certain line of code. Line 21 comes after line 20, but it might also come after line 30 if line 30 has a `goto 21`.
But, semantically, there's not a lot of difference between:
int myfunc(int *ptr) {
if (ptr == NULL) {
return 1;
}
return 0;
}
and:
int myfunc(int *ptr) {
int result;
if (ptr == NULL) {
result = 1;
goto END;
}
result = 0;
END:
return result;
}
When someone calls `myfunc`, and it returns, you don't know if it returned from the end of the function, or from the middle of the function. A function with multiple returns is a function full of gotos; or as my dad used to say: "Functions always have a single entry point; they should have a single exit point."
I've never been a fan of the "only one return" view. I find it far easier to understand when the "preconditions" are checked at the beginning of the function and, if they fail, bail out early (guard clauses). Then there's less mental load in the rest of the function.
I do this too. :P Especially in a language like Go or C where you don't have exceptions, I think the "fast fail" at the start of the function is a reasonable "exception to the rule". Otherwise you can quickly end up in a nested if statement hell.
But back in the days when I was doing embedded work, I frequently came across problems where someone allocated some resource at the start of a function, freed the resource at the end of the function, but then either failed to realize there was a return in the middle of the function, or someone else came along later and added a return to the middle of the function; either way we ended up with a resource leak. I saw this pattern over and over again. "Fast fail" is reasonable design pattern, so long as it's at the top of the function and you're careful not to allocate things before you check for preconditions, but functions of the form:
blah
blah
if
blah
blah
return x
else
blah
blah
return y
Yeah, I'm pretty sure those leaks are the origin of it. At some point it became a "best practice" without understanding the reasons behind it, and people started applying it everywhere without any thought to "why".
> "Functions always have a single entry point; they should have a single exit point."
Kind of moot as soon as you are using a language with exceptions, where by definition every function can have surprise exit points at every point where they call another function.
Furthermore, the whole "goto is evil" ordeal has been debunked by the top professionals in our field [1] and discussed many times here on HN [2,3,4,5].
I think it’s important to reflect on old practices and consider why they were important, and whether they still are.
Many of the reasons for only having a single return were that it made it much easier to reliably clean up resources allocated by the function. Given a language with garbage collection, or even simply try, catch, and finally, provide a better way to handle this. The mistake Javascropt makes is allowing a return in a finally block.
From an assembly focused point of view, multiple "return" functions executing is madness. Logically it makes far more sense for the flow of execution to be fully controlled by the return and absolutely depart the function.
I now want languages who's spec allows this insanity to have a sanitizer like C#'s to prevent more footguns.
nit: 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.
Totally 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).
OP 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`.
> 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`.
I’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?
This makes a point for separating the semantics of (a) control flow (a premature exit, similar to break in a loop) and (b) assigning return values.
However, JavaScript actually started with rather quirky semantics for return: if any exit returned a value, all exits had to return a value, otherwise an error was thrown. Yet another effect of the nexus of control flow and assignment of a return value. (This got fixed with the introduction of the return object as workaround, which was, I think, in ECMA-Script 3.)
F# lacks an imperative "return" at all - a function returns precisely the value of the expression that is its body - which makes it a lot easier to think about this kind of thing, at the cost of sometimes having to be explicit about assigning a value to the result of a `try/with`.
Blogs about "return" inside "finally" in Java and the likes were common in the 2000s. The fact that languages have so many simple things like this that surprise people indicates that there is a problem somewhere. I'm sure some of the older school languages (like VB maybe) had some insane semantics like an interrupted return will return null, I can't remember them in that much detail.
I finished reading this and felt that it was annoying clickbait. The example it started with (that I saw in a preview before clicking) was just misleading
I found finally is useful for kicking off a job after returning a response within the same func without having to spawn another process or block. i.e. return a 200 response immediately in the catch then kick your slow running job off in the finally. They can poll back for results when baked.
I doubt I know a single person that knows this (me included, until now at least). Fortunately I've never seen this actually being used. It's a bit troubling that the promise version behaves differently because most people view try/catch to be generally equivalent to the code that uses promises. I'm making a note to link to this article the first time I see it in a PR :-).
Not sure I'm call them odd -- it prints the same thing that would be printed without the "returns" (i.e., if the blocks contained only console.log calls), which is what I'd expect it to print: 'one' followed by 'three'. You're maybe thrown off because, while only one return statement can successfully return a value from the function, more than one can be executed, with their values evaluated.
You very much can return `undefined`, it's just that `return` only works inside of a function, not if you just copy the block of code into the console :)
This reminds me of something that would be included in a JS engineering interview. I find questions like this to be ridiculous. Knowing the answer to a question about this 'gotcha' would be a pretty terrible signal about future engineering performance. I'll focus on writing good software and solving complex problems, thanks.
After reading this article, I feel slight annoyance. But maybe that is just me.
EDIT: Well, except that if there was more after asyncWork() the return in the try block would exit someAsyncThing, whereas the return in the catch block just returns from the catch. Is that his point?
77 comments
[ 3.5 ms ] story [ 145 ms ] threadHaving worked at the BBC, I'm a fan of the Reithian principles; Educate, entertain, and inform. Yes, there's a central point to the article, but I like using that as a starting point to look at related things, like async functions, and the promise stuff.
TL;DR: It's an article, not a tweet :)
And yet, you know someone will and they'll feel very clever for it.
Somebody who knows the good parts of JavaScript is a JavaScript expert. You want somebody who can write good JavaScript, and using the bad parts well has nothing on using the good parts adequately.
By knowing all the bad parts, you’ll be able to write much better code, because you know what to look out for.
And yet, I expect this will soon appear in those "JS is weird, wtf" listicles.
I think C# does not allow any flow control in finally blocks, so no continue or break either (well for continue and break they're allowed if the statement they "refer to" is also in the finally block, but flow control can't go through or escape from the block).
Essentially in C# a finally block can only be escaped from by falling off of the block's end.
Looks like a good minimum example to show that the intuitive natural language semantics of try and finally don't work with statements like continue that change control flow.
I try returning but finally it continues the loop.
Literally reads as written?
There are a few languages where functions have an actual ret variable you can explicitly use.
I had something like this in the article originally, but then you also need to say that `catch` also does `_result = undefined`, and it felt messy.
- 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):
[2] https://tc39.es/ecma262/multipage/ecmascript-language-statem...`return` and `throw` set the function's result. `catch` automatically unsets the function's result. How `finally` works can be explained from that.
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.
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.
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.
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-statem...
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.
When I was a wee lad, learning basic and C, my father tried to impress upon me that a function should never have more than one `return` statement.
Back in those days, all these languages had `goto` statements, and we were told never to use them, because they were bad. But goto isn't bad because "goto" is a bad word - it makes your life unnecessarily complicated. A goto makes it very hard to reason about code, because you never know how you got to a certain line of code. Line 21 comes after line 20, but it might also come after line 30 if line 30 has a `goto 21`.
But, semantically, there's not a lot of difference between:
and: When someone calls `myfunc`, and it returns, you don't know if it returned from the end of the function, or from the middle of the function. A function with multiple returns is a function full of gotos; or as my dad used to say: "Functions always have a single entry point; they should have a single exit point."But back in the days when I was doing embedded work, I frequently came across problems where someone allocated some resource at the start of a function, freed the resource at the end of the function, but then either failed to realize there was a return in the middle of the function, or someone else came along later and added a return to the middle of the function; either way we ended up with a resource leak. I saw this pattern over and over again. "Fast fail" is reasonable design pattern, so long as it's at the top of the function and you're careful not to allocate things before you check for preconditions, but functions of the form:
are (IMHO) an anti-pattern.Kind of moot as soon as you are using a language with exceptions, where by definition every function can have surprise exit points at every point where they call another function.
Furthermore, the whole "goto is evil" ordeal has been debunked by the top professionals in our field [1] and discussed many times here on HN [2,3,4,5].
[1] https://koblents.com/Ches/Links/Month-Mar-2013/20-Using-Goto...
[2] https://news.ycombinator.com/item?id=18484221
[3] https://news.ycombinator.com/item?id=19959592
[4] https://news.ycombinator.com/item?id=14540088
[5] https://news.ycombinator.com/item?id=8760518
Many of the reasons for only having a single return were that it made it much easier to reliably clean up resources allocated by the function. Given a language with garbage collection, or even simply try, catch, and finally, provide a better way to handle this. The mistake Javascropt makes is allowing a return in a finally block.
Disallowing them is more work.
I don't dislike C# forbidding flow control in finally blocks, but I can well understand language designers not caring.
I now want languages who's spec allows this insanity to have a sanitizer like C#'s to prevent more footguns.
Both err's? (the argument and the one you define)? How about:
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`.
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.
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.
Like closing/releasing a database connection.
Sub f() 'returns 1
f = 1
End
I always thought this was insane, but it does make the semantics clear. Maybe not so insane after all
However, JavaScript actually started with rather quirky semantics for return: if any exit returned a value, all exits had to return a value, otherwise an error was thrown. Yet another effect of the nexus of control flow and assignment of a return value. (This got fixed with the introduction of the return object as workaround, which was, I think, in ECMA-Script 3.)
Right! Those are the "odd" numbers! ;)
You're trying to return the result of console.log, which is undefined, which you cannot return explicitly
After reading this article, I feel slight annoyance. But maybe that is just me.
We can tell by your notation. Mixing try blocks with async/await and promises is mixing patterns and obscuring what you are attempting to accomplish.
EDIT: Well, except that if there was more after asyncWork() the return in the try block would exit someAsyncThing, whereas the return in the catch block just returns from the catch. Is that his point?