Javascript: What's more efficient, IF block or TRY/CATCH? - javascript

I'd love some other opinions on what's more efficient in this code. Basically in the following code, there's a setInterval loop and I need 4 requirements to be true before the code runs in the loop. So in v.1 I wrote an if statement checking all 4. Worked fine.
Then I switched to just using try/catch, with the code I want to execute sitting in try{}. The logic was that during each loop, an exception would be generated but suppressed for each invalid condition. On the final loop where all conditions are true, the code executes and clears the interval.
Either works. I like the try/catch method because there's less conditional code that I need to write and worry about breaking. But I'm worried that try/catch is really inefficient, especially in a setInterval() loop hitting at 100ms. What are some opinions of other bright minds here on SO?
Try/Catch
var intvar = setInterval(function(){
try{
clearInterval(intvar);
jQuery('#'+nav[pageid].t1+'>a').replaceWith(jQuery('<span>'+jQuery('#'+nav[pageid].t1+'>a').text()+'</span>'));
//set display classes for nav
jQuery('#'+nav[pageid].t1).addClass('selected').find('#'+nav[pageid].t2).addClass('subselect'); //topnav
jQuery('#'+nav[pageid].t3).addClass('selected').find('#'+nav[pageid].t4).addClass('subselect'); //leftnav
}catch(err){}
},100);
IF Block
var intvar = setInterval(function(){
if(typeof jQuery == 'function' && typeof nav == 'object' && typeof pageid != 'undefined' && typeof document.getElementById('leftnav') == 'object'){
clearInterval(intvar);
jQuery('#'+nav[pageid].t1+'>a').replaceWith(jQuery('<span>'+jQuery('#'+nav[pageid].t1+'>a').text()+'</span>'));
//set display classes for nav
jQuery('#'+nav[pageid].t1).addClass('selected').find('#'+nav[pageid].t2).addClass('subselect'); //topnav
jQuery('#'+nav[pageid].t3).addClass('selected').find('#'+nav[pageid].t4).addClass('subselect'); //leftnav
}
},100);

Exceptions should be used for exceptional circumstances (i.e. things that you don't expect to happen normally). You should not, in general, use exceptions to catch something that you can test for with an if statement.
Also, from what I understand, exceptions are much more expensive than if statements.

Use the if statement. I don't know what the overhead is for a TRY/CATCH, but I suspect it's far greater than evaluating a boolean expression. To hit the TRY/CATCH you will have to: execute a statement, generate an error [with that associated overhead], log the error (presumably), made a stacktrace(presumably), and moved back into the code. Additionally, if you have to debug code near those lines the real error could get obfuscated with what you are TRY/CATCHing.
Furthermore, it's a misuse of TRY/CATCH and can make your code that much harder to read. Suppose you do this for longer or more obfuscated cases? Where might your catch end up?
This is referred to as Exception handling
EDIT: As commented below, you only take the runtime performance hit if you actually cause an exception.

The other answers are correct, try/catch is for exceptional circumstances and error handling. if conditions are for program logic. "Which is faster?" is the wrong question.
A good rule of thumb, if you're doing nothing with the exception, it's probably not an exception!
To figure out which to use, let's break down your if condition.
typeof jQuery == 'function' Is the jQuery() function defined?
typeof nav == 'object' Does the nav global variable contain an object?
typeof pageid != 'undefined' Is the pageid global variable defined?
typeof document.getElementById('leftnav') == 'object' Does the document contain a leftnav element?
The first is clearly an exception. You ain't getting far without a jQuery() function.
The second is also an exception. You're not going anywhere without a nav object.
The third is an exception. You need a pageid to do anything.
The fourth is probably logic. "Only run this code if there is a leftnav element". It's hard to tell because the rest of the code doesn't reference a leftnav element! Only the comments do, a red flag. So it's probably a programming mistake.
So I'd probably do this (with apologies if I'm butchering jQuery):
var intvar = setInterval(function() {
// If there's no leftnav element, don't do anything.
if( typeof document.getElementById('leftnav') != 'object') {
return;
}
try {
clearInterval(intvar);
jQuery('#'+nav[pageid].t1+'>a')
.replaceWith(jQuery('<span>'+jQuery('#'+nav[pageid].t1+'>a').text()+'</span>'));
//set display classes for nav
jQuery('#'+nav[pageid].t1)
.addClass('selected')
.find('#'+nav[pageid].t2)
.addClass('subselect'); //topnav
jQuery('#'+nav[pageid].t3)
.addClass('selected')
.find('#'+nav[pageid].t4)
.addClass('subselect'); //leftnav
}
catch(err) {
...do something with the error...
}
},100);
...but I'd really examine if the leftnav element check is applicable.
Finally, I can't help but comment that this "function" is working with global variables. You should instead be passing nav and pageid into the function in order to maintain encapsulation and your sanity.

I would write the following code:
var startTime = (new Date()).getTime();
for (var i=0; i < 1000; ++i) intvar();
var endTime = (new Date()).getTime();
alert("Took " + ((endTime - startTime) / 1000.0) " seconds");
Then I would try both versions of intvar and see which runs more quickly. I'd do this myself but I don't have the page layout you do so my code wouldn't work.
Some stylistic comments - it doesn't seem necessary to test that jQuery is a function. If it isn't, your webpage is likely messed up such that not running the intvar code will not help you. If you rarely expect the exceptions to be thrown, I'd use the try/catch.

For the provided example where you are wrapping a try/catch around a block of code that should always run anyway (unless if something horrible happens), it is good form to use try/catch. An analogy for you: do you always test "Is the sky Blue?" in your if statement, or would you wrap it in a try/catch that is triggered only when the sky happens to turn Green.
Use the If statement method if you are dealing with user provided input or if the chances of a function not existing is much higher due to something else happening in the code.
Keep in mind that if you don't trigger the exception you don't have any unwinding or backtracking across code. In the example the catch will only execute if something is wrong (jQuery is missing or some such thing), however the if statement method has an evaluation happening on EVERY SINGLE CALL to that function - you shouldn't do more work than you have to.

To directly answer the question, as everyone else has, the try..catch is likely going to be more expensive if there actually is an error.
To point out some additional errors in the code beyond what others have already pointed out:
These two codes are not at all equivalent. To explain, even though the codes appear to do the exact same thing, they do not.
In the case of the if() check, NONE of the code is executed. In the case of the exception handler, each line of code inside the exception handler will be executed. SO, what happens if the error occurs in the second, or the third line? Then you've got something completely different happening in your code than what you get if you check the conditions before executing any of it.

Aside from the answers given, I want to add some thoughts on this subject.
Exceptions that are unforeseen, i.e. the runtime throws an exception, should not be caught with try ... catch because you want to read the message that is thrown in the console.
Try ... catch should IMO be thrown when an exception occurs that your application can foresee in your application logic and you want to perform some custom actions when they do. I.e. you want to be descriptive about it being an exception, and it's not part of the happy flow of the application logic.
For example, you could have a method that validates user input so you could have an isValid method that returns a boolean in which case you would use if ... then.
On the other hand if your performing a certain process, and you know that a certain exception can occur that interrupts the happy flow and you want to handle this, I feel that it is better to throw an exception.
As abstract example, you could have methods that implements some business rules.
Instead of validating every business rule for validity, you could have one custom Exception containing dynamic metadata that can be thrown by each of these methods when validity is breached and handle them appropriately, and continue the happy flow otherwise.
This would translate into something like:
throw new BusinessValidationException(TAG, 'Reason for the validation');
Which IMO is much more descriptive than:
if (!checkValidityBusinessRule())
// doSomething
For a series of business rules.
As for the example, it's my humble opinion that this check shouldn't be happening in the first place and that the given checks are true by default for implementing the method.
if(typeof jQuery == 'function' && typeof nav == 'object' && typeof pageid != 'undefined' && typeof document.getElementById('leftnav') == 'object')
What it means is that you must declaratively ensure that given conditions are true before invoking the given method with side-effects, taking away the necessity to perform the checks at all.
If it so happens that one of the conditions is false, then it will throw an exception to the console, which is actually what you want because as a developer it means that something in your application logic or lifecycle is broken.
My 2 cents, I hope they make sense.

Related

Correct javascript syntax for function

I've been looking to at some old javascript on a website I run - hands up, I know very little about javascript.
I came across the following function:
var timeout = null;
function textareaResize() {
if (typeof(timeout))
clearTimeout(timeout);
timeout = setTimeout(function () {
refreshAutoGrowFields();
}, 200);
}
The intellisense tells me that the syntax typeof(timeout) is invalid, to be more specific 'unexpected constant condition', however this means nothing to me.
I'd appreciate it if someone could explain what should actually be there (if the intellisense is correct in its assumptions).
I'm not sure where that function was found, but it's definitely confusing, because it's actually inaccurate JavaScript.
What it's trying to do is make whats called a timeout, meaning a performance of a certain action or collection of code after a period of time.
In order to do this, it's attempting to set this "timeout" function to a pre-defined variable called "timeout".
The thing is, before they are setting it to the variable "timeout", for some reason they want to make sure that timeout hasn't already been set. Perhaps by setting it twice to setTimeout, it would potentially cause multiple functions to happen within the same amount of time (but perhaps not).
Anyways, in order to do that, they were trying to check if "timeout" has been set before.
The problem is the way they are checking it typeof(timeout), is completely incorrect, for 2 reasons.
First, the term "typeof" isn't usually to be used like a function call; rather, it is a keyword, much like var or return, so it shouldn't in general be used with ()s like it's used here, instead it should be used like typeof timeout == "null", but the problem is that even such a syntax would be completely invalid, because even if it was null, it wouldn't evaluate to "null", it would evaluate to "object", just try pasting the following JavaScript into a console:
typeof null
It should say "object", even typeof undefined is "undefined", which is a string, which, when put in an if statement, would still evaluate to true relatively speaking, so the if statement checking if timeout is defined or not was done completely wrong.
Instead, that if statement should simply be changed to if(timeout), removing typeof completely.
typeof will return the type of the variable you put inside.
These are some types in JavaScript
number
string
boolean
array
object
undefined
you can use the type as
let s1 = 'somevalue'
cosole.log(typeof(s1)) // prints 'string'
you can play around with that and change the contents of s1. Now to the comparing part, if you try something like
if(typeof(s1)=='number')
dothis()
else if (typeof(s1)=='string')
dothat()
tip : both are same
typeof s1
typeof(s1)
and typeof(typeof(someVar)) is always string, that's why the == operator works
Hope it helped and didn't confuse you further.

How to make JavaScript code more bullet-proof?

I come from a compiled-language background (C/C++/Objective-C) and am currently writing a full-fledged application in JavaScript. (TypeScript actually, but my question is the same for both.)
The problem I'm running into is that if a single error occurs, the entire flow of execution halts. For instance, if I have:
myFunction()
{
doSomethingA();
doSomethingB();
doSomethingC();
}
then if doSomethingA() has something like this:
var myValue = window.myData.myValue;
but "myData" doesn't exist on "window" at the time, then all code STOPS EXECUTING... doSomethingB() and doSomethingC() do not execute, and a console error is logged. That might be fine for simple web pages, but I'm creating an application, and it needs to not 'stop working' inexplicably.
Granted, I can use try/catch to be 'aware' of this error, but that STILL doesn't solve my problem: I would like to write the code in a way such that doSomethingB() and doSomethingC() continue to execute, even if a problem arises.
However, this is a huge over-simplification of my code. Imagine there are MANY of these functions. It would be impractical to surround each with its own separate try/catch block. Even if I did, I need the rest of a given function to continue to execute even if something in the first part fails.
Of course, I can 'protect' my variables by using:
if ( typeof window.myData != "undefined")
or
if (window.myData.hasOwnProperty("myValue")
but that becomes very messy if you have several levels to check, such as when accessing:
var myValue = window.myData.something.anotherLevel.somethingElse.value;
Now I have to check if myData, something, anotherLevel, and somethingElse are all valid before accessing this value 'safely'. This results in very ugly code.
Is there a better way to make my code more 'bullet-proof'? If a property is unexpectedly missing, I need code to continue executing after the problem statement. Is that possible without 'protecting' every single statement that accesses data that has even a tiny chance of being undefined?
That's just how JavaScript is. It's not a strongly-typed language. You have the right approach checking for falsy/undefined. You could look into a utility library, something like Lodash's isUndefined() method to help ease the pain a bit. They have a lot of helper methods like that such as checking for object types, arrays, object literals, etc.
https://lodash.com/docs#isUndefined
Normally You have to have some set of "nested if hells" like:
if(typeof window.myData !== 'undefined' && typeof window.myData.something !== 'undefined' && typeof window.myData.something.anotherlevel !== 'undefined' ...) {
}
To ease developers pain there is utilities for example lodash
it's very helpful and shortens Your code.
it has _.get(object, path) function that takes element by path and if not found it just returns undefined.
<script src="https://raw.githubusercontent.com/lodash/lodash/4.13.1/dist/lodash.js"></script>
<script>
window.myData = {
something: {
anotherlevel: {
value: 'YOU GOT ME! (:'
},
zerolevel: {
value: 0
}
}
};
function getDataByPath(path) {
var result = _.get(window, path);
alert(result);
}
</script>
<button onclick="getDataByPath('myData.something.anotherlevel.value')">
anotherlevel.value
</button>
<br/><br/>
<button onclick="getDataByPath('myData.something.zerolevel.value')">
zerolevel.value
</button>
<br/><br/>
<button onclick="getDataByPath('myData.something.unexistentlevel.value')">
unexistentlevel.value
</button>
Your best bet would be to check, if the variables you use are undefined or not with
typeof window.yourVariable === "undefined"
It's however not very safe to rely on variables in the window object, use scopes and closures.
I'm accepting Prefix's answer, since I don't like accepting my own answers. And, it's useful to hear about the Lodash helper, and know that the "nested if hells" I have are a normal (sad to hear it!) part of JavaScript.
However, the 'solution' I've gone with that has ended up making me feel very happy, is to actually reject a premise in my original question: that try/catch is not a good solution.
Turns out try/catch, combined with attempting to make sure variables are undefined, is a good way to catch those cases that I've missed.
I've actually ended up architecting it as follows. Let's say I have lots of functions I want to happen as a result of doStuff(), and I've put them into window.callbacks. I can then do (untested pseudocode, but it gives you the gist of it):
doStuff() {
for (var myFunc in window.callbacks) {
if (window.callbacks.hasOwnProperty(myFunc) ) {
try {
window.callbacks[myFunc].call();
}
catch(err) {
console.log("%cCaught error in " + myFunc + ": " + (err.stack || err), 'background: #222; color: #bada55');
}
}
}
}
This logs a unique console log message, complete with stack trace, showing what triggered the error, and yet CODE EXECUTION CONTINUES. Subsequent callbacks will still be called. The only code that doesn't execute is the remainder of the callback that caused the error.
Given my compiled-language background, this gives me warm fuzzy feelings... it means that at worst, a tiny function will fail to execute, not an entire code path following an error.

How can I detect use of invalid properties in javascript?

I'm learning Javascript and I wrote the following code:
if (mystring.len > 0) {
// do stuff
}
I accidentally used .len instead of .length. To my surprise, no error was raised. mystring.len returned undefined and this made the comparison fail but the code kept right on running. I would prefer an actual error to be raised so I can fix the code. Adding "use strict" didn't help, nor did jslint.
I know there are ways to actively check whether or not a property exists, but that's not what I'm looking for. I want Javascript to tell me when I've made a typo in a property name.
Is there a way to cause Javascript to give an error in this case?
Nope - that is how JavaScript works and it's incredibly useful. Who is to say that checking len is something that needs fixing? Consider:
if(mystring.len === undefined) {
mystring.len = "Test";
}
The best you can do is to simply check that the thing is defined before using it:
if(mystring.len !== undefined) {
}
I appreciate the strangeness, and how it doesn't feel robust (having originally come from a C# background) but there isn't a lot you can do unfortunately. The fact that JavaScript is case sensitive makes this even more frustrating. You will learn to live with it though!
If you really really wanted to run some static analysis then you could considering creating a transpiler (e.g. Babel) extension to run this sort of analysis - but it would get really difficult if you ever expected something to be undefined which I find is common place.
edit
Here's a real example that I'd use where undefined is useful. I'm working with a library that needs to move stuff from one location to another. It can't do that unless the original location has been specified, so I might write something like the following, initializing values if I don't have them for some reason:
function update(node) {
if(node.x === undefined) { node.x = 0; }
node.y = node.y || 0; // This is the shorthand way I'd actually write it
// Do some other stuff
};
"use strict" (in my experience) is used so that variables that aren't explicitly declared/instantiated that are then referenced will throw errors (else, JS would just make a new var on the fly). So that wouldn't help here.
This sounds like an error that would typically be picked up by a compiler in other languages, but since JS is interpreted, you won't have that kind of explicit error checking unless you're in a beefy IDE. Are you using a text editor or something to write JS?
Thats not the way JavaScript considers your above code. Every variable in JS is an object. So, when you do mystring.len, its actually trying to access the len property of mystring obj and when it doesn't find that property, it will return undefined - which is how it should be. Thats why you will not be able to find any error using JSLint.
Just to give you an example -
var myObj = {name: 'Hello', id: 1};
console.log(myObj.name); // Returns 'Hello'
console.log(myObj.text); // 'undefined'
In order to prevent such code from giving you any errors, you can easily use the hasOwnProperty() method like follows-
if(myObj.hasOwnProperty('text')) doSomething();
Since myObj doesn't have any property text, the doSomething() function will never be called.
This is the behaviour of JavaScript as mentioned by many/all answers. However there is an option to prevent new properties you might want to try:
Object.seal https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/seal
The simple answer is JavaScript is not ment to be typesafe...You shouldn't check it, but if you still want to check you can do it by:
if ('len' in mystring){
}
You should look into Typescript if you ask this question...

Detecting when any variable in a large JS program is set to NaN

I have a large, messy JS codebase. Sometimes, when the app is being used, a variable is set to NaN. Because x = 2 + NaN results in x being set to NaN, the NaN it spreads virally. At some point, after it has spread pretty far, the user notices that there are NaNs all over the place and shit generally doesn't work anymore. From this state, it is very difficult for me to backtrack and identify the source of the NaN (and there could very well be multiple sources).
The NaN bug is also not easily reproducible. Despite hundreds of people observing it and reporting it to me, nobody can tell me a set of steps that lead to the appearance of NaNs. Maybe it is a rare race condition or something. But it's definitely rare and of uncertain origins.
How can I fix this bug? Any ideas?
Two stupid ideas I've thought of, which may not be feasible:
Write some kind of pre-processor that inserts isNaN checks before every time any variable is used and logs the first occurrence of NaN. I don't think this has been done before and I don't know how hard it would be. Any advice would be appreciated.
Run my code in a JS engine that has the ability to set a breakpoint any time any variable is set to NaN. I don't think anything does this out of the box, but how hard would it be to add it to Firefox or Chrome?
I feel like I must not be the first person to have this type of problem, but I can't find anyone else talking about it.
There is probably no solution for your problem aka: break, whenever any variable is set to NaN. Instead, you could try to observe your variables like this:
It was earlier stated, that the Chrome debugger offers conditional breakpoints. But, it also supports to watch expressions. In the Watch-Expressions menu you can set a condition to break, whenever the variable is set to a specific value.
Object.observe is a method that observes changes on a object. You are able to listen to all changes on the object, and call debug when any variable is set to NaN. For example, you could observe all change on the window object. Whenever any variable on the window object is set to NaN, you call debug. Please note, that Object.observe is quite cutting edge and not supported by all browsers (check out the polyfill in this case).
Take this opportunity to write a test case for every function in your code. Perform random testing and find the line of code that can create NaN values.
Another problem of yours is probably how to reproduce this error. Reloading your webpage over and over doesn't make too much sense. You could check out a so called headless browser: It starts an instance of a browser without displaying it. It can be leveraged to perform automatic tests on the website, click some buttons, do some stuff. Maybe you can script it in such a way that it finally reproduces your error. This has the advantage that you don't have to reload your webpage hundreds of times. There are several implementations for headless browsers. PhantomJS is really nice, in my opinion. You can also start a Chrome Debug Console with it (you need some plugin: remote debugger).
Furthermore, please notice that NaN is never equal to NaN. It would be a pity if you finally are able to reproduce the error, but your breakpoints don't work.
If you're doing a good job keeping things off of the global namespace and nesting things in objects, this might be of help. And I will preface this by saying this is by no means a fully complete solution, but at the very least, this should help you on your search.
function deepNaNWatch(objectToWatch) {
'use strict';
// Setting this to true will check object literals for NaN
// For example: obj.example = { myVar : NaN };
// This will, however, cost even more performance
var configCheckObjectLiterals = true;
var observeAllChildren = function observeAllChildren(parentObject) {
for (var key in parentObject) {
if (parentObject.hasOwnProperty(key)) {
var childObject = parentObject[key];
examineObject(childObject);
}
}
};
var examineObject = function examineObject(obj) {
var objectType = typeof obj;
if (objectType === 'object' || objectType === 'function') {
Object.observe(obj, recursiveWatcher);
if (configCheckObjectLiterals) {
observeAllChildren(obj);
}
} if (objectType === 'number' && isNaN(obj)) {
console.log('A wild NaN appears!');
}
};
var recursiveWatcher = function recursiveWatcher(changes) {
var changeInfo = changes[0];
var changedObject = changeInfo.object[changeInfo.name];
examineObject(changedObject);
};
Object.observe(objectToWatch, recursiveWatcher);
}
Call deepNaNWatch(parentObject) for every top level object/function you're using to nest things under as soon as they are created. Any time an object or function is created within a watched object/function, it itself will become watched as well. Any time a number is created or changed under a watched object--remember that typeof NaN == 'number'--it will check if it's NaN, and if so will run the code at console.log('A wild NaN appears!');. Be sure to change that to whatever sort of debugging output you feel will help.
This function would be more helpful if someone could find a way to force it onto the global object, but every attempt I made to do so simply told me I should sit in time out and think about what I've done.
Oh, and if it's not obvious from the above, on a large scale project, this function is bound to make pesky features like "speed" and "efficiency" a thing of the past.
Are your code communicate with your server side, or it is only client side?
You mention that it is rare problem, therfore it may happend only in some browsers (or browsers version) or on any situation which may be hard to reproduce. If we assume that any appearance of nan is problem, and that when it happend user notice bug ("there are NaNs all over the place"), then instead display popup with error, error should contain first occurence of nan (then users may raport it "Despite hundreds of people observing it and reporting it to me"). Or not show it, but send it to server. To do that write simple function which take as agument only one variable and check if variable is NaN,. Put it in your code in sensitive places (sensitive variables). And this raports maybe solate problematic code. I know that this is very dirty, but it can help.
One of your math functions is failing. I have used Number(variable) to correct this problem before. Here is an example:
test3 = Number(test2+test1) even if test1 and test2 appear to be numbers
Yeah man race conditions can be a pain, sounds like what it may be.
Debugging to the source is definitely going to be the way to go with this.
My suggestion would be to setup some functional testing with a focus on where these have been reproduced, set some test conditions with varied timeouts or such and just rerun it until it catches it. Set up some logging process to see that backtrace if possible.
What does your stack look like? I can't give too much analysis without looking at your code but since its javascript you should be able to make use of the browser's dev tools I assume?
If you know locations where the NaNs propagate to, you could try to use program slicing to narrow down the other program statements that influence that value (through control and data dependences). These tools are usually non-trivial to set up, however, so I would try the Object.observe-style answers others are giving first.
You might try WALA from IBM. It's written in Java, but has a Javascript frontend. You can find information on slicer on the wiki.
Basically, if the tool is working you will give it a program point (statement) and it will give you a set of statements that the starting point is (transitively) control- and/or data-dependent on. If you know multiple "infected" points and suspect a single source, you could use the intersection of their slices to narrow down the list (the slice of a program point can often be a very large set of statements).
(was too long for a comment)
While testing you could overwrite ALL Math functions to check if an NaN is being produced.
This will not catch
a = 'string' + 1;
but will catch things like
a = Math.cos('string');
a = Math.cos(Infinity);
a = Math.sqrt(-1);
a = Math.max(NaN, 1);
...
Example:
for(var n Object.getOwnPropertyNames(Math)){
if (typeof Math[n] === 'function') Math[n] = wrap(Math[n]);
}
function wrap(fn){
return function(){
var res = fn.apply(this, arguments);
if (isNaN(res)) throw new Error('NaN found!')/*or debugger*/;
return res;
};
}
I didn't tested, maybe an explicit list of the "wrap"ed methods is better.
BTW, you should not put this into production code.

Exceptions in javascript, should I use them and how?

I've been told a few times that it's "bad" to use exceptions in javascript. Haven't really been told why it's bad, but inestead that I should use break, continue and return instead.
Which is fine, except I don't need to return/break/continue, I need to throw. I have a case where I have iterator functions nested in each other, each one returns the next value on call, so I use an exception to indicate that there's nothing more to iterate : seems like a logical way to do it, makes the code clean and works perfectly in practice. Is there really any reason not to use exceptions in js?
Second question, when I'm using exceptions, what kind of objects should I throw? For errors I'll obviously throw instances of Error, but for special cases (stop iteration, etc) I needed a quick way to check for those specific exceptions, and what I did is simply define an empty named function (function StopIteration(){}) and since functions are compared by reference I can always check if it's my special case or I should just rethrow. Is there a better or more idomatic way to do this in js? Should I really try to refactor my code avoid using exceptions?
Thank you
It sounds like you're searching through a multidimensional space using nested loops, which is a fairly common thing to program. It's tempting to throw an exception from the innermost loop when the target is found which then gets caught by a "catch" block around the outermost loop but this often considered poor practice.
Language designers are aware of this pitfall so they allow us to give labels (names) to loop structures so that we can break from them (or continue to them). Consider this example:
function findValueInMatrix(value, matrix) {
var r, c, coords, rows=matrix.length, cols=matrix[0].length;
found: // Label the outer loop as "found" so we can break from it.
for (r=0; r<rows; r++) {
for (c=0; c<cols; c++) {
if (matrix[r][c] == value) {
coords = [r, c]
break found; // Exit the loop labeled "found".
}
}
}
return coords;
}
You can find more information in this post about breaking from nested loops.
This jsPerf test case also demonstrates that breaking to a label is nominally faster than throwing an exception (presumably in most browsers).
exception handling is in general slower than normal evaluation and only for exceptional situations
for stopping iterations you are better of defining a hasNext or isEmpty function for an ending condition or return a sentinel value (like undefined) when there are no more elements so the loop becomes
//with hasNext
while(it.hasNext()){
var val = it.next();
//...
}
//or with a sentinal undefined
while( (val = it.next()) !== undefined){
//...
}
For some people, throwing exceptions to indicate that there is no more values is a bad style; exceptions are made for exceptional behaviour.
Moreover, exception handling is generally slower than simple tests.
The difference is really minor (and maybe does not even exist) if the thrown instance is not created everytime. So having a constant StopIteration and throwing it is in fact quite fast.
For your problem, you might want to see https://developer.mozilla.org/en/JavaScript/Guide/Iterators_and_Generators (they use exceptions to stop iteration).
I found that in dynamically typed languages exceptions are used for this kind of purposes too.
Throwing an exception to break out of a nested recursive search/walk is the easiest and more elegant way to go.
Say you walk a tree, recursivelly calling a function to handle the current node, calling again for each of its children and so on.
As you process each node, you might want to completelly break the whole call stack and return the found value (or stop iterating in general), breaking out of all the loops that are waiting in previous calls. Mind you that these previous calls might be costly in processing & memory, but useless if you "found" your result. Throwing an Exception is the most efficient and simple way to achieve this, but of course it needs attention.
For simple (even nested) loops its a overkill and is rightly touted as an anti-pattern.

Categories