DEV Community

I Use For Loops Almost Always In Javascript

Dwayne Charrington on January 19, 2020

Recently, Kent C. Dodds posted about an instance where using a for loop dramatically resulted in better performance. When you delve into how fil...
Collapse
 
ryansmith profile image
Ryan Smith • Edited

I usually opt for a loop due to simplicity and readability, not necessarily performance. I found the premature optimization comment that you received pretty shallow because of that. With something like .reduce(...), there is mental overhead to parse it and figure out what is going on. If it isn't calculating some type of sum or total, then it is being used only for the loop aspect. In that case, just use a loop!

It seems like many JavaScript devs look at a collection that they want to loop over and say "hmmm how can I use a SICK reduce on this". It is clever and shorter, but is it more readable? Most of the time, I would disagree.

JavaScript is a language that has some functional features incorporated into it, which is great. But I think taking that to the extreme and trying to make everything functional is not always the best approach. I'm not sure why the functional stance has become so prevalent in the JavaScript community.

Collapse
 
beggars profile image
Dwayne Charrington

You hit the nail on the head here, Ryan. I have no idea where the functional mindset originated from. I know quite a few JS "thought leaders" are strong advocates for the functional approach and I honestly think React shares a lot of the blame for this in recent years.

Because of design choices and mistakes in React itself lending itself to things like hooks being created, their anti-classes/oop approach has resulted in an ecosystem of developers who don't question the approach and blindly assume that Facebook is the be-all and end-all of JS and follows them into the mouth of the volcano.

Just based on my experiences (and sounds like yours as well) I have seen what happens when "clever coders" think they are writing "clever code" and it always almost results in the code being rewritten eventually because it's trying to be too smart and nobody can work out what it is doing.

I have seen a lot of people advocating for using map as a means of looping over items in arrays where a for loop would be more appropriate. And in the case of reduce especially, I have seen some pretty crazy stuff.

And truth to be told, I have fallen into some traps with reduce before forgetting the first argument in the initial value or the previous value, because I am used to how methods like forEach return the value in the iteration first.

Collapse
 
albertomontalesi profile image
AlbertoM

My ex colleague used to use fancyreduce all the time which made it so much harder for me as a Junior to understand what was going on. Fancy one-liners are cool on codewars or similar sites but not so much in actual code that everybody in the team needs to understand.

Collapse
 
budyk profile image
Budy

Agree

Collapse
 
drmikecrowe profile image
drmikecrowe

So, I've noticed this pattern in my code a lot:

const ary = await Promise.all(...);
for (let elem of ary) {
    ...
}

Knowing that for ... of loops are not as fast, I started using the following construct:

const ary = await Promise.all(...);
while (ary.length) {
    let elem = ary.shift();
    ...
}

This post got me wondering how my loop compares, so I tested my way and other loop constructs here. It's interesting how much slower these other loop constructs are. Needless to say, I'm going back to the traditional for loop now!

Here are my simple test results:

Testing vanilla:      4.1ms
Testing lodash:      25.4ms -- 518% slower
Testing es6-for-of:   7.3ms --  78% slower
Testing forEach:      6.1ms --  48% slower
Testing map:          9.2ms -- 125% slower
Testing reverse:      3.4ms --  17% faster
Testing shift:       20.9ms -- 408% slower
Collapse
 
rjbudzynski profile image
rjbudzynski

I ran your tests on a longer array, and found increasing the array length magnifies the performance advantage of the vanilla for-loop quite a bit:

Testing vanilla:     120.9ms
Testing lodash:      2561.8ms -- 2019% slower
Testing es6-for-of:  347.6ms -- 188% slower
Testing forEach:     1179.2ms -- 875% slower
Testing map:         1517.9ms -- 1156% slower
Testing reverse:     166.4ms --  38% slower
Testing shift:       7316.3ms -- 5952% slower

I used an array of length 1000, and since I'm impatient I cut down LOOPS to 1e5.

Also to be noted is that the 'shift' loop is not equivalent to the others, because it destroys the input array in the process. I dealt with that (and with creating a long array) by setting up

const someNumbers = Array.from({ length: 1000 }, (v, i) => i);

on the top level, and copying it into testArrayShift() on each iteration of the outer loop.

It runs a bit against my intuition that the reverse loop becomes slower on longer arrays -- I expected it to have a (small) performance advantage.

Actually I'm used to writing the reverse loop as

for (let i = someNumbers.length; i--; ) {
    tmp += someNumbers[i];
}

(and that's the version I actually tested)
but again a surprise: your version is actually a bit faster.

Altogether, I guess I'll just stick to vanilla for-loops from now on.

Collapse
 
rjbudzynski profile image
rjbudzynski

I added .reduce() to the test, and on a long array it's only about twice as slow as the vanilla for-loop.

Collapse
 
vonheikemen profile image
Heiker

For loops can be clean. In ES6 we got for..of loops, they are declarative and work with more data types than just arrays.

for (let variable of iterable) {
  // statement
}

My only problem with for loops is that it takes discipline to extend their behavior in a "clean way." It is just so easy to go and add little if statement here and there (specially if you are under pressure) and before you know it you have block of code that is dificult to deal with.

Collapse
 
beggars profile image
Dwayne Charrington

I love for..of loops, they are the only kind of for loop I really write these days. Especially being able to do something like for (const value of object.entries()) which makes working with different types of data (not just arrays) so much nicer.

There are definitely some traps you can fall into (side effects from modifying the reference if you forget to create a new array/collection) but if you're aware of them, it's not much of a problem.

Collapse
 
kimsean profile image
thedevkim

Thank you for this article. I've used a lot of for loop, and recently i feel like using for loop makes me something like an "outdated" js coder. But yeah thanks, now when I use for loop I have the mindset that it gives better performance, and the future of js frameworks is all about performance.

Collapse
 
beggars profile image
Dwayne Charrington

Definitely nothing wrong with using for loops. I do recommend knowing and learning the functional methods filter, map and reduce but you don't have to use them in most cases.

It's sad that JS has got to a point where a staple of every language (a for loop) has become something many in the community argue against and seemingly for no good reason.

Collapse
 
kimsean profile image
thedevkim • Edited

well around 2-3 months ago i always use for loop almost all the time, but then since I started using reactjs, I use now filter and map only if don't need a break statement

Collapse
 
albertomontalesi profile image
AlbertoM

I rarely use reduce because I never remember how to use it lol. The reason why I would use filter or map over a normal loop is for readability. If I see a filter I can already guess what I was trying to do in that part of the code rather than seeing a for loop and guessing. That being said I'm not working on large data so performance is never really a big concern.

Collapse
 
craigmc08 profile image
Craig McIlwrath

That last paragraph really hits the nail on the head. Javascript was designed so that there are many ways to write things, so obviously the best way to write something is how you prefer it.

These people who care more about everyone doing what they are doing than actually writing good code are toxic.

Collapse
 
edmundmunday profile image
edmundmunday

I'd definitely agree with you for Reduce, I find the way that works and is structured to just be inherently confusing.

Map and Filter though I will usually default to, rather than a for loop. For me it's all about reducing intellectual overhead and ensuring my hands can keep up with my brain so that I don't break my flow.

Collapse
 
madhadron profile image
Fred Ross

At one point I was somewhat well known in the Haskell community, so functional programming feels very natural to me. And I only use for loops in JavaScript, since I have found the error messages and debugging support for map/filter/fold to be too unpleasant to use.

Collapse
 
brianmcoates profile image
Brian

Really good stuff. This is similar to what I heard Kyle Simpson say he is so intentional about what variable declaration he uses and where. It was cool he said that when he is using a variable that will be scoped and used though out a function he would use var to declare the variable but when creating a for loop and declaring a variable inside of it or to declare his iterator he would use let. He explained the more in the code he sticks to this thought process hopefully the easier it is to read. It really inspired me to not just ever say one thing fits all like always use === or filter or reduce they all have their place let's just be intentional.

Collapse
 
valentinprgnd profile image
Valentin Prugnaud 🦊

Nothing wrong with loops indeed. However I find filter, reduce and map useful as they are immutable and can be chained. How do you deal with these use cases?

Collapse
 
mooshallski profile image
mooshallski

This topic has been worked so many times. I don't know what Kent did. Maybe he itereated over the array first to filter and then did iteration for reduce and in terms of for loo only one iteration was done. Don't know. But the truth is that in case of loop you should almost always use filter/map/reduce. First of all it's always better to have a declarative code. It produces less confusion, which as Kyle Simpson says - is a reason of most bugs. I'm not talking about one-liners vs couple line of code. Experienced programmer should know that it's not the amount of code that counts. First of all obviously the name of this array methods makes you closer to quick understand what youre about to see. You look at the code and it's a matter of second when you notice: OK, so we have some mapped data, filtered one, reduced to one value etc, whereas with for loops you know absolutely nothing about those iterations until you work them through line by line. You can't really know the outcome, purpose, anything. Secondly it requires some of the good practises of programming when using map/filter/reduce. It's rare to see some bloated code within this methods. If one sees that during code review than it's clear that someone made it wrong. In for loops there are so many place where you can make sth wrong, where somebody may overlook what you wanted to do. There is no really required structure, it probably won't be pure. When it comes to breaking the loops, you can also do that without for. Just simply use Array.prototype.every. It will do pretty much same thing.