56

How can Javascript duplicate the four-part try-catch-else-finally execution model that other languages support?

A clear, brief summary is from the Python 2.5 what's new. In Javascript terms:

// XXX THIS EXAMPLE IS A SYNTAX ERROR
try {
  // Protected-block
} catch(e) {
  // Handler-block
} else {
  // Else-block
} finally {
  // Final-block
}

The code in Protected-block is executed. If the code throws an exception, Handler-block is executed; If no exception is thrown, Else-block is executed.

No matter what happened previously, Final-block is executed once the code block is complete and any thrown exceptions handled. Even if there’s an error in Handler-block or Else-block and a new exception is raised, the code in Final-block is still run.

Note that cutting Else-block and pasting at the end of Protected-block is wrong. If an error happens in Else-block, it must not be handled by Handler-block.

0

6 Answers 6

49

I know this is old, but here is a pure syntax solution, which I think is the proper way to go:

try {
    // Protected-block
    try {
        // Else-block
    } catch (e) {
        // Else-handler-block
    }
} catch(e) {
    // Handler-block
} finally {
    // Final-block
}

The code in Protected-block is executed. If the code throws an error, Handler-block is executed; If no error is thrown, Else-block is executed.

No matter what happened previously, Final-block is executed once the code block is complete and any thrown errors handled. Even if there’s an error in Handler-block or Else-block, the code in Final-block is still run.

If an error is thrown in the Else-block it is not handled by the Handler-block but instead by the Else-handler-block

And if you know that the Else-block will not throw:

try {
    // Protected-block
    // Else-block
} catch(e) {
    // Handler-block
} finally {
    // Final-block
}

Moral of the story, don't be afraid to indent ;)

Note: this works only if the Else-handler-block never throws.

Sign up to request clarification or add additional context in comments.

5 Comments

Nice and clean, without incurring additional complexity or dependencies. I like this solution.
Much better than adding a success variable... unless you want any errors raised in the else block to bubble up and not be caught by the main handler block, which is what the Python syntax allows. If that's what you need, I don't see any way without some sort of success variable.
Quite late to the party, but this doesn't work for testing.
This is very similar to how it was in python <2.4 stackoverflow.com/a/35505895/5422525
What is the solution if I want to treat exceptions in the Else-block as fatal?
34

Extending the idea of jhs a little, the whole concept could be put inside a function, to provide even more readability:

var try_catch_else_finally = function(protected_code, handler_code, else_code, finally_code) {
  try {
    var success = true;
    try {
      protected_code();
    } catch(e) {
      success = false;
      handler_code({"exception_was": e});
    }
    if(success) {
      else_code();
    }
  } finally {
    finally_code();
  }
};

Then we can use it like this (very similar to the python way):

try_catch_else_finally(function() {
  // protected block
}, function() {
  // handler block
}, function() {
  // else block
}, function() {
  // final-block
});

5 Comments

Totally. I was thinking of doing a quick NPM module to do this. I left this out of my answer because you have be careful since the this value will change vs. the simple nested try. However my second example also has that error.
Right. We'd probably want to bind the this of the caller to the four callbacks. Let's call it an exercise for the reader ;)
Just happened back across this. Note, in newer versions of JavaScript, the "fat arrow" notation makes the syntax even more readable.
This works, but you lose control flow. You can't return from the try, continue loops, etc.
If you attempt to return from the main block, you will end up executing the else-block even though you shouldn't have...
12

I know the question is old and answers has already given but I think that my answer is the simplest to get an "else" in javascripts try-catch-block.

var error = null;
try {
    /*Protected-block*/
} catch ( caughtError ) {
    error = caughtError; //necessary to make it available in finally-block
} finally {
    if ( error ) {
        /*Handler-block*/
        /*e.g. console.log( 'error: ' + error.message );*/
    } else {
        /*Else-block*/
    }
    /*Final-block*/
}

Comments

10

Javascript does not have the syntax to support the no-exception scenario. The best workaround is nested try statements, similar to the "legacy" technique from PEP 341

// A pretty-good try/catch/else/finally implementation.
try {
  var success = true;
  try {
    protected_code();
  } catch(e) {
    success = false;
    handler_code({"exception_was": e});
  }
  if(success) {
    else_code();
  }
} finally {
  this_always_runs();
}

Besides readability, the only problem is the success variable. If protected_code sets window.success = false, this will not work. A less readable but safer way uses a function namespace:

// A try/catch/else/finally implementation without changing variable bindings.
try {
  (function() {
    var success = true;
    try {
      protected_code();
    } catch(e) {
      success = false;
      handler_code({"exception_was": e});
    }
    if(success) {
      else_code();
    }
  })();
} finally {
  this_always_runs();
}

1 Comment

for the first example - you can always use new Boolean() true/false instead of success = false|true - new Boolean() creates boolean object which when compared against existence returns true in contradiction to literal false which when checked for existence will return false
1

Here's another solution if the problem is the common one of not wanting the error callback to be called if there is an uncaught error thrown by the first callback. ... i.e. conceptually you want ...

try { 
    //do block 
    cb(null, result);
} catch(err) {
    // err report
    cb(err)
}

But an error in the success cb causes the problem of cb getting called a second time. So instead I've started using

try { 
    //do block 
    try { 
        cb(null, result); 
    } catch(err) { 
        // report uncaught error 
    }
} catch(err) {
    // err report
    cb(err)
}

which is a variant on @cbarrick's solution.

Comments

0

One way to do this in JS without needing to track success in a separate variable or needing nested functions is to use a labeled break:

function divide(x, y) {
    if (y === 0) {
        throw new RangeError("Division by zero")
    }
    return x / y
}

function divideWithTryCatch(x, y) {
    tryDivision: {
        let result
        try {
            result = divide(x, y)
        } catch (e) {
            console.log("division by zero!")
            break tryDivision
        } finally {
            console.log("executing finally clause")
        }
        console.log("result is", result)
    }
}

divideWithTryCatch(10, 2)
// executing finally clause
// result is 5

divideWithTryCatch(10, 0)
// division by zero!
// executing finally clause

If an error is thrown, control flow moves to after the labeled block. Note that here, the "else" runs after the finally unlike in the Python version.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.