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
});
Related
I've run into the strange assertion in a book in my point of view. I guess that I don't understand something but anyway it will be great if you shed light on the situation.
ajax('<host1>/items',
items => {
for (let item of items) {
ajax(`<host2>/items/${item.getId()}/info`,
dataInfo => {
ajax(`<host3>/files/${dataInfo.files}`,
processFiles);
});
}
});
An author pay attention on :
There’s another hidden problem with this code. Can you guess what it is? It occurs when you mix a synchronous artifact like a for..of imperative block invoking asynchronous functions. Loops aren’t aware that there’s latency in those calls, so they’ll always march ahead no matter what, which can cause some really unpredictable and hard-to-diagnose bugs. In these situations, you can improve matters by creating closures around your asynchronous functions, managed by using forEach() instead of the loop.
Instead of it they offer the following:
ajax('/data',
items => {
items.forEach(item => {
// process each item
});
});
Frankly speaking I expected that if we use let for loop it means we create a closure for each iteration therefore I don't see any hidden problems there.
You are correct, if the author's comment is on that exact code snippet, they were mistaken.
Loops aren’t aware that there’s latency in those calls [...] you can improve matters by [...] using forEach()
That changes nothing, forEach() is equally unaware of async calls made inside its callback as a for loop is of async calls made in its body. forEach() will "always march ahead" the same way a for loop will.
With let you cannot encounter the issue that the author seems to be worried about, as each iteration of the loop has its own item just like when using items.forEach( item => { ... .
Even with var there is no issue with that code, since the variable item is not used inside the callback to the ajax request. You could produce the author's concern by using var and using item inside the callback, such as: console.log( item.getId() );.
Note: It is important to be aware that the callbacks will most likely run in a different (seemingly random) order than they were initiated in. If you aren't aware of that it can cause surprising bugs, but that also has nothing to do with using a loop vs. forEach.
The authors of that book seem to have no clue. There is no problem of for (let … of …) that .forEach(…) would fix.
They talk about
creating closures around your asynchronous functions, managed by using forEach() instead of the loop
but the closure is not created by the forEach callback function, the closure is the callback passed into the ajax function. It closes over the surrounding scope, and there is hardly any difference between the for block scope (when using let or const) and the function body scope (when using forEach).
I have a large array containing various data, and a function that performs some basic but somewhat time-consuming analysis on the array.
I use a forEach loop to iterate over the array. I want the function to return a specific element if a condition is met, otherwise false, using Promises since the function could take a considerable amount of time to complete:
var analyzeData = function (arr) {
return new Promise(function (resolve, reject) {
var answer = false;
arr.forEach(function (elm) {
if (elm.foo == true) {
answer = elm;
}
});
resolve(answer);
});
}
The issue I'm encountering is that I suspect that answer is being resolved before the forEach loop completes; i.e. before the forEach loop can finish checking every element's foo property, the function resolves to answer, which is by default false.
Is there a way to ensure that the code following the forEach loop waits for the loop to finish iterating, or loop through the array in some other manner?
JavaScript loop iteration is synchronous, and in your code you will always complete the loop before the call to resolve is made.
Note that JavaScript is also single threaded, so putting this in a promise won't make it run in the background. There will still be some period of time in which the UI not responsive to the user while this loop is executing.
You can offload some computation to background processes using web workers.
As an aside: can you exit the loop when the first item is found? If not, why not iterate in reverse (using a for-loop, not array reversal) and exit when the first item is found? It is still technically O(N) in the worst case, but the best case is O(1) if you bail out as soon as possible.
However to be more helpful, you will need to provide more information about what is happening. I would suggest putting together a small example on http://jsfiddle.net/ -- that can be a useful exercise in and of itself.
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.
Overview
So I have pulled out a document from my database. Inside is a nested collection of objects. Each of the objects inside this nested collection has an '_id' attribute. I want to find one on these objects specifically by its '_id' using Javascript.
Example
http://jsfiddle.net/WilsonPage/tNngT/
Alternative Example
http://jsfiddle.net/WilsonPage/tNngT/3/
Questions
Is my example the best way of achieving this?
Will this block in Node.js?
Yes, if you only know a specific value which is contained by one of your objects (which are held in an Array) you need to loop over the whole structure and compare those values.
As you also did right, break the iteration when you found one (return in your example).
So my answer to your first question would be yes, in terms of performance this is the right and best way.
What I don't get is the "Async" example. You just moved the code and changed the structure. Your code is still "blocking" since you're using a normal for-loop to search. If that array would be huge, it would block your node-app for the amount of time the loop needs to finish.
To really make it asynchronously, you would need to get rid of any loop. You would need to loop over the structure with a runway-timer.
var findById = function(collection, _id, cb){
var coll = collection.slice( 0 ); // create a clone
(function _loop( data ) {
if( data._id === _id ) {
cb.apply( null, [ data ] );
}
else if( coll.length ) {
setTimeout( _loop.bind( null, coll.shift() ), 25 );
}
}( coll.shift() ));
};
And then use it like
findById( myCollection, 102, function( data ) {
console.log('MATCH -> ', data);
});
This technique (which is a simplified example), we are creating an self-invoking anonymous function and we pass in the first array item (using .shift()). We do our comparison and if we found the item we are looking for, execute a callback function the caller needs to provide. If we don't have a match but the array still contains elements (check for the .length), we create a timeout of 25ms using setTimeout and call our _loop function again, this time with the next array item, because .shift() gets and removes the first entry. We repeat that until either no items are left or we found the element. Since setTimeout gives other tasks in the JS main thread (on a browser, the UI thread) the chance to do things, we don't block and screw up the whole show.
As I said, this can get optimized. For instance, we can use a do-while loop within the _loop() method and also use Date.now() to do things until we go over the 50ms mark for instance. If we need longer than that, create a timeout the same way and repeat the above described operation again (so its like, do as much operation as possible within 50ms).
I'd pre-sort the array by each item's _id and at least implement a binary search, if not something a little more sophisticated if speed is really an issue.
You could try using binary search, in most cases it's faster than linear search. As jAndy said, you will still block with standard for loop, so take a look at some node asynchronous library. First that falls to my mind is async.js
I messed around with async.js to produce this solution to my problem. I have tried make it a reusable as possible so it is not just locked down to search the '_id' attribute.
My Solution:
http://jsfiddle.net/WilsonPage/yJSjP/3/
Assuming you can generate unique strings from your _id you could hash them out with js's native object.
findById = (collection, _id, callback, timeout = 500, step = 10000)->
gen = ->
hash = {}
for value, i in collection
hash[value._id] = value
unless i % step then yield i
hash[_id]
it = gen()
do findIt = ->
{done, value} = it.next()
if done then callback value
else
console.log "hashed #{(value/collection.length*100).toFixed 0}%"
setTimeout findIt, timeout
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.