Return statement supressess error in script

The following tip belongs in the obscure category, and is worth understanding, because it can easily lead to mysterious problems in your code.

Requirement
Write a sub function that returns a boolean on wether a record exists or not in a BC, and do further processing based on the result.

The logic would look something like this.

function doesRecordExist()
{
var bExist = false;

try {
//query bc logic
//if record exists
bExist = true;
} catch (e) {
throw "doesRecordExist > Error: " + e.toString();
} finally {
return bExist;
}
}

There is nothing wrong with this code, and it works very well... except when there is an error.

So, what happens if we introduce a deliberate error in the code?

function doesRecordExist()
{
var bExist = false;

try {
var DummyObject = {};
DummyObject.dummyMethod();

//query bc logic
//if record exists
bExist = true;
} catch (e) {
throw "doesRecordExist > Error: " + e.toString();
} finally {
return bExist;
}
}

What we've done, is declared a dummy object and called a non existent method, this will cause it to error and jump to the catch statement.

The error will be thrown, but when it hits the finally statement, the function returns the boolean back to the originating caller and supresses the error from our sub function. This is very undesirable for obvious reasons.

In this scenario, the error will never make it out of this function alive... unless the exception happens in the finally block as seen in the solution below.

function doesRecordExist()
{
var bExist = false;
var sError = "";

try {
var DummyObject = {};
DummyObject.dummyMethod();

//query bc logic
//if record exists
bExist = true;
} catch (e) {
sError = e.toString();
throw "doesRecordExist > Error: " + sError;
} finally {
if ( sError.length > 0 ){
throw sError
}
return bExist;
}
}

In the above code, the system will raise the first exception which will be caught by the catch statement, it will then go into the finally and we re-throw the exception back to the caller. We can then bubble the error back up to the root function and handle the error there.

A simpler solution would be to put the return statement at the end of the function body, but purists will insist the return statement should go into the finally block.

function doesRecordExist()
{
var bExist = false;

try {
var DummyObject = {};
DummyObject.dummyMethod();

//query bc logic
//if record exists
bExist = true;
return bExist;
} catch (e) {
throw "doesRecordExist > Error: " + e.toString();
} finally { }
}

Another alternative solution comes from our friend, the creator of the ABS framework.

function doesRecordExist()
{
var bExist = false;
var bError = "";

try {
var DummyObject = {};
DummyObject.dummyMethod();

//query bc logic
//if record exists
bExist = true;
} catch (e) {
bError = true;
throw "doesRecordExist > Error: " + e.toString();
} finally {
if ( !bError ){
return bExist;
}
}

In this solution, we throw the error inside the catch, and in the finally block, we check if an exception occurred and prevent the function from return a value and therefore suppressing the exception, otherwise if no exception occurred, we allow the function to return the boolean, and we continue processing normally.

This behaviour may seem elementary, but it can easily trap junior and senior programmers alike, and is well worth recognising.


Reactions:

2 comments:

  1. I'm pretty sure, you just need to put the return statement outside the finally block:

    function doesRecordExist()
    {
    var bExist = false;

    try {
    //query bc logic
    //if record exists
    bExist = true;
    } catch (e) {
    throw "doesRecordExist > Error: " + e.toString();
    } finally {
    //null objects
    }

    return bExist;
    }

    ReplyDelete
  2. yes this works, its similiar to the second solution where the return statement is at the end of the try.

    the moral is that if there is a return statement in the finally block, it can suppress errors, if not handled.

    ReplyDelete

Comments are open to all, please make it constructive.