2

I would like to jump out of an outer function from inside an inner function.

something = true

outer: (next)-> 
    @inner (err)-> 
        if err?
            next err 
            #jump out of outer function here

    console.log 'still in outer'

inner: (next)-> 
    next new Error 'oops' if @something is true

The code is in coffeescript but javascript answers are welcome.

Update

Thanks for the quick replies - so how about using the return value of the @inner function? Is there a generally accepted pattern for this kind of thing?

something = true

outer: (next)-> 
    return unless @inner (err)-> next err if err
    console.log 'still in outer'

inner: (next)-> 
    if @something is true
        next new Error 'oops'
        return false
    return true
biofractal
  • 18,963
  • 12
  • 70
  • 116

1 Answers1

0

If the reason you want to immediately exit outer is that an exceptional condition occurred (an error), you can throw an exception. If outer doesn't catch it, outer will be terminated (as will outer's caller, and so on, until something either catches the exception or the JS engine itself does).

For normal program flow, though, in JavaScript and the various languages that transpile into it, we don't use exceptions for normal program flow (not least because they're expensive). For those cases, no, there's no way for a called function to terminate its caller; it has to either return a value that caller uses to know to exit, or set a value on a variable that's in scope for both of them.

Here's your example updated to use inner's return value, which would be the most normal way to do this:

something = true

outer: (next)-> 
    stop = @inner (err)-> 
        if err?
            next err 
            #jump out of outer function here

    if stop
      return
    console.log 'still in outer'

inner: (next)-> 
    next new Error 'oops' if @something is true
    if someCondition
      return true

Here's your example updated to use a variable they both close over:

something = true

stop = false

outer: (next)-> 
    @inner (err)-> 
        if err?
            next err 
            #jump out of outer function here

    if stop
      return
    console.log 'still in outer'

inner: (next)-> 
    next new Error 'oops' if @something is true
    if someCondition
      stop = true

(Please excuse my CoffeeScript, I don't use it.)

T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875
  • Where is the value of the `stop` flag being set in your examples? – biofractal Aug 01 '14 at 13:24
  • @biofractal: Er, um...I seem to have forgotten that bit in my struggles with CoffeeScript. My *first* example, which I made up entirely (this was before your updated code) had it! :-) Let's see, one sec... – T.J. Crowder Aug 01 '14 at 13:26
  • 1
    Great, thanks. And look, I have got you writing CoffeeScript at last. They said it would never happen :-) – biofractal Aug 01 '14 at 13:34
  • @biofractal: :-D I *do* feel a strong need to go wash my hands now... ;-) *(Just a joke. I think the proliferation of languages that transpile to JS is a good thing.)* – T.J. Crowder Aug 01 '14 at 13:40
  • evidence that the claim "exceptions are expensive" is false: https://stackoverflow.com/questions/63089089/returning-from-outer-function-inside-nested-function-using-throw/63144123#63144123 – mathheadinclouds Jul 28 '20 at 23:33
  • 1
    @mathheadinclouds - Throwing *errors* used to be much more expensive than it is now (but it's still expensive on some engines). Throwing non-errors isn't, particularly (no need to collect stack information), which is why I never claimed it was when answering your question. I think we've said all that needs saying to each other on this, let's just leave it there, thank you. You want to use `throw` for mainline execution? Knock yourself out, it's *your* code. – T.J. Crowder Jul 29 '20 at 07:30