Reputation: 77
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
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):
var numberofitems = (payload.data.form_values.b691 || []).length;
var numberofitems = ((payload.data.form_values || {}).b691 || []).length;
var numberofitems = (((payload.data || {}).form_values || {}).b691 || []).length;
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
Reputation: 5955
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
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:
payload
payload.data
payload.form_values["b691"]
payload.form_values["b691"].length
(call this v1)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