Hamsandwich
Hamsandwich

Reputation: 77

How to make a function to perform try/check without causing an error?

I have a lot of try...catch statements for instances where an incoming JSON packet may not have data in sections where other JSON may. To do this, I normally do the following:

var numberofitems;
try {
    numberofitems = payload.data.form_values["b691"].length;
} catch (err) { numberofitems = 0; }

However there are a lot of instances where I might get a null value which messes up my code later. To clean up my code, I have written a function which handles the error in one line:

function checkTry(toCheck, errVal) {

  try {
    var result = toCheck;
  } catch (err) {
    result = errVal;
  }
  return result;
}

function datacheck() {

  var data;

  var result = checkTry(data.length, "0");
  console.log(result);

}

datacheck();

Then my example becomes the following:

var numberofitems = checkTry(payload.data.form_values["b691"].length,"0");

The problem is that payload.data.form_values["b691"] throws an error as I try to pass it into the function, resulting in the error I was originally trying to catch. Is there any way around this?

I have made a JS Bin snippet to play with the issue.

Upvotes: 2

Views: 129

Answers (3)

0Valt
0Valt

Reputation: 10355

Solution

Use default values and ditch the try...catch statement completely. An exception should really be an exception, something you do not know how to handle (at least try...catch has no place in simple object destructuring):

  1. var numberofitems = (payload.data.form_values.b691 || []).length;
  2. var numberofitems = ((payload.data.form_values || {}).b691 || []).length;
  3. var numberofitems = (((payload.data || {}).form_values || {}).b691 || []).length;
  4. var numberofitems = ((((payload || {}).data || {}).form_values || {}).b691 || []).length;

In-depth look

The technique is based on the behaviour of logical operators. For logical AND, if the left-hand side is truthy, it is returned, and the right-hand side is not evaluated, if left-hand side is falsy, the right-hand side is returned. The inverse is true for logical OR operator.

Since undefined and null are both falsy and objects are always truthy, we can safely assume that if an object is given, logical OR will return that object and only fallback to evaluating our default value in case no object was provided in the first place.

Be aware that the technique is limited to always truthy / falsy values. For example, the following is indeterministic in terms of what value holds:

const indeterministicTest = () => {
  const rand = Math.floor(Math.random() * 3);
 
  const value = rand === 1 ? 
  null : rand ? true : false;
  
  console.log( value || `The value is ${value}, and I am a fallback string!` );
}

let timeoutId = null;

function runIndefinitely (callback) {
  timeoutId = setTimeout( () => {
    callback();
    runIndefinitely(callback);
  }, 1e3);
}

const start = document.querySelector("#start");
start.addEventListener("click", () =>  {
  runIndefinitely(indeterministicTest);
});

const end = document.querySelector("#end");
end.addEventListener("click", () => {
  timeoutId && clearTimeout(timeoutId);
});
<button id="start" type="button">Start</button>
<button id="end" type="button">End</button>

Upvotes: 3

Bekim Bacaj
Bekim Bacaj

Reputation: 5955

you shuld try the following function which I wrote about the time when AJAX came out and it works with hastables well formed namespace hierarchies as in (continue reading)

Introducing seamless guesswork in JavaScript Determine if a namespace/variable-name exists. Check for namespace existence in your environment variables and objects before writing to them.

Guess if "Book1.chapter22.paragraph37" exists without provoking errors and retrieve/send data if it exists with the simplest expression :

Call syntax:

    isNS( [string], [num] )

Example:

var nsresult =
    isNS( "Book1.chapter22.paragraph37", -1 );
    if( nsresult[0] ){ send( nsresult[1] ) else notify( nsresult[4], nsresult[2] ) };

etc etc

// omit modifier or 0, false and undefined to get a Boolean

isNS( "myObject.property.subProperty.myMethod" );
_> Boolean : true/false

Modifier Possible values:

_> -1; (0|false|undefined); ( 1|true ); 2; 3 or 4.

Description:

(-1 ) _> returns array object containing complete inoculation.

( 0 ) _> optional; returns Boolean: true (if complete NS chain exist) : false (if it's broken).

( 1 ) _> if NS* query exists, case true: returns the existing value; if not, case false: returns the last valid context if any, or the global object if root doesn't exist.

( 2 ) _> returns an array of existing namespaces in the argument query.

( 3 ) _> returns an array containing all specified namespaces.

( 4 ) _> returns the original query string.

function isNS(arg,f) { /*b.b. Troy III p.a.e.*/
    var i, a = arg.split("."), c = this, s = [], b, r;
    f = f || 0;
        for( i in a ) {
            c ? a[i] in c ? ( c = c[ a[i] ], s.push( a[i] ), b = !0 ) : b = !1 : 0;
        }
    r = [ b, c, s, a, arg ];
    return f < 0 ? r : r[+f||f]
}

*NS - stands for already familiar "Namespace" initials. [This makes but a tinny cluster on some real A.I. yet -very important one!]

;
function isNS(arg,f) { /*b.b. Troy III p.a.e.*/
        var i, a = arg.split("."), c = this, s = [], b, r;
        f = f || 0;
            for( i in a ) {
       c ? a[i] in c ? ( c = c[ a[i] ], s.push( a[i] ), b = !0 ) : b = !1 : 0;
            }
        r = [ b, c, s, a, arg ];
        return f < 0 ? r : r[+f||f]
    }
 ;   
 book1 = { 
   chapter1 : { 
       paragraph1 : ["first line","second line"],
       paragraph2 : ["first line","second line"],
       paragraph3 : ["first line","second line"] },
  chapter2 : { 
       paragraph1 : ["first line","second line"],
       paragraph2 : ["first line","second line"],
       paragraph3 : ["first line","second line"] },
  chapter3 : {  
       paragraph1 : ["first line","second line"],
       paragraph2 : ["first line","second line"],
       paragraph3 : ["first line","second line"] }
 }
;

    console.log( 'Boolean for:"book1.chapter2.paragraph3"' )
;
    console.log( isNS("book1.chapter2.paragraph3", 0 ) )
;
    console.log( 'Retrieve:"book1.chapter2.paragraph3"' )
;
    console.log( isNS("book1.chapter2.paragraph3", 1 ) )
;

Upvotes: 1

Hikash
Hikash

Reputation: 429

So, you can't do that. If you think like a compiler for a second, you realize the problem: Every statement needs to be evaluated before it can be used, and any error needs to be handled immediately. In your checkTry call, your statements are executed as follows:

  1. evaluate payload
  2. evaluate payload.data
  3. evaluate payload.form_values["b691"]
  4. evaluate payload.form_values["b691"].length (call this v1)
  5. Evaluate "0" (call this v2)
  6. call checkTry(v1, v2)
  7. assign the result of checkTry to numberofitems

If something isn't present in any the first 3 steps, then the next step will throw an error. The problem is that your try/catch function never got a chance to execute, and so will never work the way you want.

For what it's worth: I'm not sure it makes sense to try to salvage that idea. However, you can "defer" execution, in a way, by wrapping this in a function and passing that function to the checktry

I think that'd look something like this:

const risky = () => {payload.data.form_values["b691"].length}
checkTry(risky, "0")

CheckTry then needs to execute the risky function inside a try/catch. This should work, however, it's a lot of extra effort, and something you'll need to repeat over and over... kind of defeating the purpose and potentially adding an extra layer of complexity for future developers.

Upvotes: 1

Related Questions