Reputation: 1186
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
Upvotes: 6
Views: 3103
Reputation: 1055
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.
Upvotes: 0
Reputation: 156662
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).
Upvotes: 3
Reputation: 4235
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.
Upvotes: 0
Reputation: 48226
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){
//...
}
Upvotes: 2