Sunday 2 April 2023

forEach Method vs for of loop

While I'm using JavaScript here, this post applies to any language providing a forEach() iteration method as an alternative to traditional loops. I think it was in 2005 with that beautiful library called prototype.js that I first came across the possibility of iterating an array using a method (in modern JavaScript it would be the Array.prototype.forEach() method) rather than a "normal" loop construct. At that time it seemed so cool and using it was like a way of bragging. Over the years I've found myself using it more and more rarely. Of course I love all the iterate-transform methods: map, filter, etc in Array extras or lodash, but when all I need is iterating an array to apply some action I tend to use the "for of" loop.

I've been thinking lately if there's any advantage in using forEach() rather than for of. Well, indeed forEach() has some limitations. First, if your action is an async function forEach() won't work (though we saw that it's pretty simple to implement an async counterpart). Then you have to simulate "continue" using "return" (but in those cases I think it's more natural to use a normal loop), and we can't simulate "break". Of course we could write a new forEach() that treats an specific exception (for example BreakError) as a break.

When those limitations do not apply (we don't need await, break or continue), one could wonder if forEach() makes our code more declarative, more functional. I general terms, I don't think so, and I'm not alone [1] and [2]

However, there are specific cases when forEach() can make our code look better. Let's see:

- Combined with the safe navigation operator:


users?.forEach(user -> /* doWhatever */);

//probably looks better than:

if (users){
	for (const user of users) {
	 /* do whatever */
	}
}


- When you have already chained calls to other Array Extras methods



getUsers().filter(user -> user.country == "France")
	.forEach(user -> /* doWhatever */);

// maybe looks better than:

for (const user of getUsers.filter(user -> user.country == "France")){
	/* do whatever */
}


No comments:

Post a Comment