[HN Gopher] In JS functions, the 'last' return wins
       ___________________________________________________________________
        
       In JS functions, the 'last' return wins
        
       Author : jaffathecake
       Score  : 89 points
       Date   : 2021-07-03 13:17 UTC (9 hours ago)
        
 (HTM) web link (jakearchibald.com)
 (TXT) w3m dump (jakearchibald.com)
        
       | jonny_eh wrote:
       | ok, so what does this print?                   try {
       | return console.log('one');         } catch(err) {
       | return console.log('two');         } finally {           return
       | console.log('three');         }
       | 
       | Tip: The results are "odd".
        
         | edflsafoiewq wrote:
         | What's "odd" about it? It does exactly what I'd expect.
        
         | re wrote:
         | 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.
        
         | valbaca wrote:
         | Uncaught SyntaxError: Illegal return statement
         | 
         | You're trying to return the result of console.log, which is
         | undefined, which you cannot return explicitly
        
       | [deleted]
        
       | cousin_it wrote:
       | Reminds me of the unescapable loop:                   while
       | (true) {           try {             return;           } finally
       | {             continue;           }         }
        
         | moritzwarhier wrote:
         | Nice. :)
         | 
         | 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.
        
       | bobbylarrybobby wrote:
       | Reminds me of VBA, where the way to "return" a value in a
       | function is to assign it to the function's name
       | 
       | 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
        
         | masswerk wrote:
         | 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.)
        
         | TheSoftwareGuy wrote:
         | FORTRAN does the same actually
        
           | masswerk wrote:
           | As did Algol60.
        
             | benibela wrote:
             | And Pascal
        
       | the_gipsy wrote:
       | Why can't they get straight to the point?
        
         | jaffathecake wrote:
         | 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.
         | 
         | TL;DR: It's an article, not a tweet :)
        
           | ludamad wrote:
           | Just as another assurance, the article was not hard to skim
           | for the main point
        
       | cmrdporcupine wrote:
       | > Please never quiz folks about this in a job interview.
       | 
       | And yet, you know someone will and they'll feel very clever for
       | it.
        
         | swagasaurus-rex wrote:
         | On a whiteboard
        
       | ludamad wrote:
       | "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?"
        
         | jaffathecake wrote:
         | "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.
        
         | woutr_be wrote:
         | 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.
        
           | wizzwizz4 wrote:
           | I think a better question would be "what are the good parts
           | about JavaScript". _Everyone_ knows the bad parts (right?).
        
             | woutr_be wrote:
             | People know what they like about it (the good parts), it
             | takes a more experienced person to be able to explain what
             | they dont like.
        
               | wizzwizz4 wrote:
               | 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.
        
               | rgoulter wrote:
               | 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.
        
       | edflsafoiewq wrote:
       | return x can be understood as                   ret = x; //
       | assign to the implicit return variable         return;  // return
       | ret to the caller
       | 
       | If the return is cancelled by a finally, its basically just the
       | case of assigning to the ret variable multiple times; the last
       | assignment wins.
       | 
       | There are a few languages where functions have an actual ret
       | variable you can explicitly use.
        
         | jaffathecake wrote:
         | It's more of a 'result' variable, because the result can be a
         | return or a throw. If you throw, it clears the return, and vice
         | versa.
         | 
         | 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.
        
       | voidnullnil wrote:
       | 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.
        
       | valbaca wrote:
       | low effort article explaining precisely what `finally` does in JS
       | that is in no way surprising
        
       | byteface wrote:
       | 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.
        
       | dvt wrote:
       | 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-
       | statem...
        
         | dahart wrote:
         | > 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.
        
           | masklinn wrote:
           | > What is the reason for allowing returns in try-catch-
           | finally blocks?
           | 
           | 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.
        
             | mjevans wrote:
             | 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.
        
           | jwalton wrote:
           | > 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."
        
             | aardvark179 wrote:
             | 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.
        
             | RHSeeger wrote:
             | 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.
        
               | jwalton wrote:
               | 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
               | 
               | are (IMHO) an anti-pattern.
        
               | Izkata wrote:
               | 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".
        
               | [deleted]
        
               | twoodfin wrote:
               | I typically take this a step further, returning from
               | trivial or common "fast-path" cases up front and falling
               | through to any complex logic.
        
               | jonny_eh wrote:
               | This has certainly become the the common best practice.
               | I'm a fan too.
        
             | kilburn wrote:
             | > "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].
             | 
             | [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
        
           | dvt wrote:
           | > 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-
           | statem...
        
             | jchw wrote:
             | 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.
        
             | dahart wrote:
             | 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.
        
         | jaffathecake wrote:
         | The 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 wrote:
           | It's akin to how in pascal you set the return value by
           | assigning the name of function. I think. It's been decades
        
             | thijsvandien wrote:
             | Or the variable `Result` (most common nowadays), although
             | you may also call `Exit()` with a parameter.
        
           | jonny_eh wrote:
           | > `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 wrote:
             | 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.
        
         | e12e wrote:
         | > 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 wrote:
           | 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`.
        
             | e12e wrote:
             | > 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.
        
         | jchw wrote:
         | 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.
        
           | dvt wrote:
           | 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).
        
       | Smaug123 wrote:
       | 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`.
        
       | TrowthePlow wrote:
       | This seems much less about which return actually 'sends data
       | back' (terminates function execution) and more about how
       | try/finally works in general
        
       | SavantIdiot wrote:
       | > I hadn't used finally much in JavaScript until recently, where
       | I find myself using it like this in async functions:
       | 
       | 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.
        
         | jaffathecake wrote:
         | Nope. It was designed precisely for patterns like this.
        
           | SavantIdiot wrote:
           | Really? I don't follow: why is try/catch/finally preferred
           | over:                 async function someAsyncThing() {
           | startSpinner();         await asyncWork()         .catch(err
           | => {           if (err.name == 'AbortError') return;
           | showErrorUI();         })         .finally(() => {
           | stopSpinner();         });       }
           | 
           | Should be identical, no?
           | 
           | 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?
        
       | locallost wrote:
       | 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 :-).
        
       | lucideer wrote:
       | > _The same happens in Java and Python too._
       | 
       | And yet, I expect this will soon appear in those "JS is weird,
       | wtf" listicles.
        
         | karldcampbell wrote:
         | Doesn't javac issue a warning for this? I know most IDEs do.
        
         | whoisthemachine wrote:
         | 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.
        
           | masklinn wrote:
           | > 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.
        
       | ajkdhcb2 wrote:
       | 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
        
       | natenthe wrote:
       | 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.
        
       ___________________________________________________________________
       (page generated 2021-07-03 23:01 UTC)