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.
Related
In languages such as javascript or (maybe?) lua, all functions by default are treated as if they had a return statement at the end:
function() {
// do
return;
}
Is equal to
function() {
// do
}
I'm wondering if returning from an inner block at the end of the function changes anything in the core, the compiling process, the VM.
function() {
if (condition) {
return;
}
// end of function
}
Same question applies to breaking a loop:
function() {
for ( loop ) {
return;
}
// end of function
}
Does the machine "look" for anything when a loop is broken, or a condition check has ended?
This is not a stylistic question, please don't tell me to make code readable.
TL:DR / optimization advice: you don't need to do anything special to gain performance. if(condition) return inside an inner loop is typically at least as efficient as an if(condition)break; to reach the end of the function.
Putting nested loops inside a function so you can use a return as a multi-level break is a good way of being able to express the logic efficiently without a goto, easy for humans and easy for compilers/interpreters.
Making loop conditions more complicated to avoid multiple return statements in one function is not helpful for performance.
Generally no, a return in the middle of a function is not fundamentally different or more or less efficient than reaching the implicit return at the end. And an explicit return at the bottom of a function isn't special either.
(We're talking about void functions that never return a value, I assume. Obviously returning a value is different from not returning a value.)
Restructuring your code to break out of a loop and reach the implicit return at the bottom of a function is not more efficient (but could easily be less efficient in some interpreted languages, especially if they aren't JITed.) e.g. if the interpreter does a jump within the function and then has to do another jump. (A good ahead-of-time compiler or JIT optimizer could see what was happening and make good machine code anyway.)
Some compilers / interpreters may handle a return by just jumping to a common block of cleanup (epilogue) that all return statements share. But tail-duplication is possible: when compiled to machine code, a function can have multiple copies of epilogue + ret instructions, reachable from different paths.
(JavaScript implementations do typically JIT functions to machine code; IDK about LUA. And of course they can inline functions. return statements in functions that get inlined can be just plain jumps, or could get optimized away entirely.)
I'm not exactly sure whether I correctly understood your question, but I'll try to answer it from my point of view.
The return statement in the end of a function declaration indicates to leave the function and return nothing (void). If you omit a return statement, nothing would happen after the actual function execution. Thus, I think the two functions you declared behave in a different way:
function a() {
// executes until the following statement and then breaks
return;
}
function b() {
// executes all statements and afterwards leaves the context where it was called
}
Regarding your question concerning inner blocks like condition checks or loops, I guess these statements could only be "optimized" by a parser somehow if they consist of static values like numbers or strings. As soon as any dynamic values like variables occur, it would be impossible to optimize anything or have an advantage from the inner result statement.
I hope you can get the point of my explanation.
I am not sure I understand the value of the functional style looping/mapping if we can't use the break and continue keywords.
I can do this:
collections.users.models.forEach(function(item, index) {
//can't use break or continue...?
});
or I can do this:
for (var i = 0; i < collections.users.models.length; i++) {
if (user.username === collections.users.models[i].username) {
app.currentUser = collections.users.models[i];
break;
}
}
what's the advantage of the functional call if I can't use the break or continue keywords?
.each() or .forEach() are less flexible than a plain for loop. They just are.
As you have discovered, they offer you less control over the looping. They are a convenience ONLY when you want to iterate the entire set or when it really helps you to automatically have a new function context for your loop iteration code (sometimes useful in async operations) or when you enjoy the more declarative coding in that using the method expresses your coding intent a little more clearly and succinctly than the for loop. .forEach() will also automatically skip sparse elements of an array.
Other than those features, they are just a reduction in typing that sacrifices some looping control. Use them when you like one of the advantages and don't use it when you need more looping control.
FYI, for Javascript arrays, .some() and .every() attempt to get you back some looping control, though they are still not as flexible as a for loop.
If you use .some(), you can return true; to be the equivalent of break; (since that will stop the looping) and you can just return; to be the equivalent of continue; (since that will return from the callback and advance to the next iteration).
To be honest, there is not much of an "advantage" so much as a convenience. You can call return to force yourself out of a single iteration of a forEach loop, but this is not the same as the native for because you are still invoking a function. The convenience comes in the form of not having to define the parameterization of your loop (ie. start point, end point, step size) as well as providing the value of the index and item in the items iterated over.
It is worth mentioning that this convenience comes at the cost of for loop control (ie. start point, end point, step size), backwards iteration, the ability to break the entire loop, a small performance impedance, and yes, even cross browser compliance.
each expresses your intent: to iterate and perform a side effect for each item. each abstracts the process of iterating: initializing and incrementing a counter, fetching by array index. That's what the functional combinators do.
Your code snippet is a great example. Let's rewrite that in a functional style:
app.currentUser = collections.users.models.find(function (u) {
return u.username === user.username;
});
find clearly expresses your intent and abstracts the concept of iterating until you find an item matching your predicate.
You can't natively break from forEach or each callback function. However, there is a technique many people use to break from this with customized exception:
var BreakException = function(){};
try{
list.forEach(function(el){
if (meetCriteria(el)){
throw BreakException; // Break from forEach
}
});
}
catch (e){
// Catch break
if (e != BreakException)
throw e; // If not a break, but other exceptions, raise it
}
// Carry on
Simple approach above is creating a BreakException object and raise it whenever you want to break from the unbreakable loop. Catch that particular BreakException and carry on what your function does.
One thing to remember, always check if the caught exception is BreakException as you defined, since the other possible exceptions (like TypeError, etc) also got trapped by this catch statement. Don't swallow it without checking.
Using Array.prototype.some and Array.prototype.every to continue/break out of functional for loops is a good thing to do
[1,2,3,null,5].every(function(v){
if(!v) return false; // this will break out of the loop
// do your regular loopage lulz
});
In Draft ECMAScript 6 Specification, what's the rationale behind using StopIteration exception to signal the end of iteration instead of using a dedicated method (hasNext in Java/Scala, and MoveNext in C#) to check for the end of iteration.
Potential performance problem aside, in my opinion, exception should not be used for something that's not really an exception.
I don't claim to be authoritative with this answer. Instead, it is just my recollection of various discussions I read about iterators and/or my own thoughts... Unfortunately I do not have a list of sources (too long ago) nor can I easily produce one (because even googling up stuff takes lots of time sometimes).
StopIteration
The name StopIteration and a lot of the semantics originated from python.
PEP 234 has some remarks regarding alternative approaches:
It has been questioned whether an exception to signal the end of
the iteration isn't too expensive. Several alternatives for the
StopIteration exception have been proposed: a special value End
to signal the end, a function end() to test whether the iterator
is finished, even reusing the IndexError exception.
A special value has the problem that if a sequence ever
contains that special value, a loop over that sequence will
end prematurely without any warning. If the experience with
null-terminated C strings hasn't taught us the problems this
can cause, imagine the trouble a Python introspection tool
would have iterating over a list of all built-in names,
assuming that the special End value was a built-in name!
Calling an end() function would require two calls per
iteration. Two calls is much more expensive than one call
plus a test for an exception. Especially the time-critical
for loop can test very cheaply for an exception.
Reusing IndexError can cause confusion because it can be a
genuine error, which would be masked by ending the loop
prematurely.
hasNext()
Java's hasNext() is just a "bonus". Java's next(), however, has it's own flavor of StopIteration called NoSuchElementException.
hasNext() is hard, if not impossible, to implement, which is best demonstrated with e.g. the following generator:
var i = 0;
function gen() {
yield doSomethingDestructive();
if (!i) {
yield doSomethingElse();
}
}
How would you implement hasNext() for that? You cannot simply "peek" into the generator, because that would actually execute doSomethingDestructive(), which you didn't intend to do or else you would have called next() in the first place.
So you'd have to write some code analyzer that has to always reliably proof that a given code when run with a certain state will always result in either a yield or no yield (halting problem). So even if you could write something like that, the state may still change between .hasNext() and a subsequent .next().
What would hasNext() return in the following example:
var g = gen();
g.next();
if (g.hasNext()) {
++i;
alert(g.next());
}
At that point of time true would be the right answer. However, the subsequent .next() would throw, and the user would probably have a hard time figuring out why...
You could tell people: "Don't do the following things in your iterator implementation" (a contract). Which people will routinely break and complain.
So in my humble opinion, hasNext() was ill-conceived and makes it too easy to write wrong code.
.MoveNext()
C# is pretty much the same as .next(), just that the return value indicates if there was actually a next item instead of or the lack of an exception.
There is one important difference, however: You need to store the current item in the iterator itself, e.g. .Current in C#, which may prolong the life-time of the current item unnecessarily:
var bufferCtor = (function gen() {
for(;;) {
yield new ArrayBuffer(1<<20); // buffer of size 1 megabyte
}
})();
setInterval(function() {
// MoveNext scheme
bufferCtor.MoveNext(); // Not actually Javascript
var buf = bufferCtor.Current; // Not actually Javascript
// vs. StopIteration scheme
var buf = bufferCtor.next();
}, 60000); // Each minute
OK, that example is a little contrived, but I'm sure there are actual use cases where a generator/iterator would return something talking up a lot of space or otherwise occupying resources.
In the StopIteration scheme, buf can be garbage-collected as soon as it goes out of scope.
In .MoveNext(), it cannot be garbage-collected until .next() is called again, because .Current will still hold a reference to it.
Conclusion
In my opinion, of the presented alternatives, the StopIteration one is the least error prone and least ambiguous approach. And if I recall correctly, the python and es-6 people thought about the same.
Is there any benefit of doing so and can it lead to readability concerns?
Labels are generally used in JavaScript to break from an outer loop. For example:
while (true) {
while (true) {
if (someCondition) break;
}
}
In this program you'll only break from the inner loop. The outer loop will loop infinitely. Sometimes however you may wish to break from the outer loop instead of the inner loop. You use labels for this purpose:
loop: while (true) {
while (true) {
if (someCondition) break loop;
}
}
That being said if you have lots of nested loops or switch cases then using labels does in fact help with code readability even though it may not actually affect the program itself.
Take the code in this question for example: https://codereview.stackexchange.com/q/14532/15640
Here we have a switch case in a for loop and although it using we never need to break out of the for loop labelling the loop and the switch case might help some people. For others it's just a distraction.
In the end it boils down to your style of programming. Beginners usually like to label everything. More experience programmers find too many comments and labels annoying.
Steve Yegge actually wrote an entire blog post on this. You may find it very interesting: http://steve-yegge.blogspot.in/2008/02/portrait-of-n00b.html
Try coding without comments: http://www.codinghorror.com/blog/2008/07/coding-without-comments.html
While I don't believe in too many absolutes in coding, I will say that it is rare that I'd use a label in Javascript.
In fact, I don't believe I've ever used them at all the language.
Thanks to the powerful structures given, for ... for ... in while do ... while if ... else and the ability to do an early return almost anything that you'd need a label for can be written in a cleaner structure.
I'm hard-pressed to consider a time where requiring nested loops, and breaking the outer by name from within the inner is necessary, versus, perhaps a simple check of a value at the bottom of the outer loop, to decide whether to continue or not...
Typically, that structure is going to be easier to follow than break third_from_outer_loop; or continue loop_that_is_two_loops_higher.
If you're at a point where you're ready to break the outermost loop from the innermost, then why not just return early?
This question already has answers here:
Using 'return' instead of 'else' in JavaScript
(13 answers)
Closed 5 years ago.
In the following example - given that the return value isn't of any importance - is there a reason to prefer either method over the other?
// Method 1
function (a, b) {
if (a == b){
// I'm just interested in
// the stuff happening here
} else {
// or here
}
return true;
}
// Method 2
function (a, b) {
if (a == b){
// I'm just interested in
// the stuff happening here
return true;
}
// or here
return true;
}
It seems that best practices (mostly by places I've worked for) is to set default values at the top of a method or function and only change those values if some condition occurs. Thus, the use of else is not needed so Method 2 is preferred.
Since the example is JavaScript, special attention needs to be paid in regards to code size. So Method 2 would create less code for the same functionality, furthering its argument as the preferred.
However, if you have more than 2 possible conditions, an else or else if cannot be avoided. However, most places I've worked prefer a Switch Case in these situations.
I would prefer Method 1 because it is less confusing to read. Also, less duplicate code.
I would base my decision on clarity of code and readability, i.e.:
Choose method 1 when you need to do more stuff in the block after the if-block.
Choose method 2 when you only need two blocks of code, it's then clearer to read
Choose method 1 again in cases where you explicitly think your readers wouldn't understand your cryptic code without the word "else"; this is common when the blocks become larger than a few lines.
Many of today's programmers consider less indentation easier to read and I agree. In which case general preference should go to using the second method.
I would recommend method 1 as it is more readable and self documented.
Any modern browser's interpreter should eliminate any performance advantage in either direction.
There are a couple of reasons method 1 is preferable that haven't been mentioned yet. Having a single point of exit makes any future modifications that require an action common to both branches easier and less likely to be buggy (because the author missed the early return. Similarly, in some cases it makes debugging easier, by providing a common place to put a breakpoint or alert().
Readability here really depends on the role of the function.
If this function will ALWAYS return true, then I would prefer the Method 1 It is clear because it only returns in one place, and it is easy to see it will always be true.
In the above case, Method 2 is more confusing. It returns in multiple places, and is more confusing thusly. Consider a developer unnecessarily traversing possible branches and then seeing how they affect the return value. In this simple case, it is not as big of a deal, but when you get more elaborate conditionals, I would really avoid this approach.
I would only use Method 2 if you have very little code in the if block. Such as something that would deal with an edge case.
Hope that helps.