I recently had a coding interview that involved evaluating one schema against another. The details of it aren't that important, but one thing that came out of it (in the middle of the interview) was that you can't break out of a forEach()
loop. I had forgotten that little tidbit and it probably screwed up my chances of getting hired. After you read this, hopefully, you won't make the same mistake I did! Don't be like me.
Video Version
If you prefer to watch than read, check out the video version of this!
MDN Knows All
As noted by MDN:
There is no way to stop or break a forEach() loop other than by throwing an exception. If you need such behavior, the forEach() method is the wrong tool
That's some hardcore sass coming from the MDN docs. However, they are right, knowing which tool to choose is important.
Before we get too deep into why you can't break out of a forEach()
, let's examine what a loop even is and where forEach()
came from.
What is a Loop
A loop in programming solves a pretty common problem: I need to run the same code against all this data. Put simply, it is:
Repeating the same code over and over (on loop) until we reach a defined end state.
The Problem
For the sake of comparison, we're going to solve the same problem using the various loop types. Here is the problem:
Compare two arrays and see if the items in them are the same.
Here is the data we are going to compare:
const jedis = ["Anakin","Luke"]
const sith = ["Palpatine", "Anakin"]
We have two arrays, both with a couple of names. You'll probably notice that Anakin is both a Jedi and a Sith. This is a trivial example, however not far off from what I was tested on during my interview.
The Old A Way
What I don't want you to get from this article is that one loop is better than another. They all offer unique programming solutions and have a spot for specific use cases. The trick is knowing which one to use when.
Traditional For Loop
If you've ever taken any type of programming course, you've probably been exposed to our good friend the for
loop. It has been a handy tool for programmers for a long time and is still useful today. Let's solve our problem using it.
// Our data again, for reference
const jedis = ["Anakin", "Luke"];
const sith = ["Palpatine", "Anakin"];
// start our loop, define our iterator variable
for (let i = 0; i < jedis.length; i++) {
// create a variable we can reference
const thisJedi = jedis[i];
// see if the item in the array we are testing exists
if (sith.includes(thisJedi)) {
// If it does exist, then that jedi is also a sith
console.log(`${thisJedi} is also a Sith`);
// we can exit out
break;
}
console.log(`${thisJedi} is not a Sith`);
}
The for loop offers a pretty handy way of exiting our code if it meets a condition we choose. This is immensely helpful when looping over a TON of data. It has been very helpful in solving some of the Project Euler problems, specifically this one.
The New Another Way
Among other things, forEach()
was stamped in the spec in 2009 along with all the other goodness that was given to us in ES5. It serves as a handy method to write clean code that easily iterates over items in an array.
What is it doing?
A forEach()
loop is a function that runs another function (callback) on each item in an array. We define what happens in that callback function. JS is nice enough to give us three parameters in that function:
- The item in the array
- The index of the item
- The whole array
Let's take a look at our problem using a forEach()
loop instead. I've included all three parameters in the function, but we're only using the first, the item, which I'm naming jedi
// We have to create a global state variable to keep track of what is happening
let matching
// loop over array
jedis.forEach((jedi,index,array) => {
// check to see if jedi is in sith
if(!sith.includes(jedi)) {
// if it isn't, set global variable to false
matching = false
}
// it keeps going...
})
console.log(matching) // false
If it makes more sense, you can refactor the callback function into a named function. I think it makes it a bit more readable. It also allows us to reuse this function wherever we want. Yay functional programming!
let matching
function isJediAlsoSith(jedi,index,array) {
if(!sith.includes(jedi)) {
matching = false
}
}
jedis.forEach(isJediAlsoSith)
Our solution essentially does the same thing. The only difference is it keeps running until it reaches the end of the jedis
array. For an array of such a small size, I doubt that it will make much of a performance difference.
But Why?
This finally brings us to the answer to our question, why can't we break out of a forEach()
loop? It's because the loop is running that callback function over every item, so even if you write a return
it's only returning on that instance of the function. It keeps going. In the case of the forEach()
function, it doesn't do anything with the returned code. Be aware, that is not the case for some of the other Array Methods.
Additionally, because of this, break
or continue
are not valid statements.
Other Ways
There are quite a few different types of loops. They all have different purposes and I'd recommend looking into each one. You don't always need a forEach()
loop.
forEach() vs map()
Likely, the most common array methods that appear in tutorials are forEach()
and map()
. The biggest difference between the two is that map
will return a new Array, while a forEach()
won't.
Traditional Loops
while loop
Array Methods
Array.forEach()
Array.map()
Array.filter()
Array.reduce()
Array.reduceRight()
Array.every()
Array.some()
Array.indexOf()
Array.lastIndexOf()
Array.find()
Array.findIndex()
Iterable Object Loops (including Arrays)
for in
for of
This is the Way
As mentioned earlier by the incredibly sassy MDN docs, choosing the right tool is paramount to success. The number of options may seem a bit overwhelming at first, but I like to take the approach of: "if it works, it's the right tool."
Generally speaking, you can refactor your code to death, but then you're just wasting time you could be building stuff. In the case of my interview, I was using the right tool, the wrong way. Had I known remembered that you can't break out of a forEach loop, things probably would have turned out different 🤷🏼♂️.
If you have any additional info share, please drop it in the comments below!
As always, happy coding.
Plugs
Book
I'm writing a book about graphic design and how it relates to software development! If you're interested, sign up here for updates.
Music
I also write music! Check it out here: Spotify | Youtube | Apple Music
https://open.spotify.com/track/4o9dZj5AF4nvPMnFFkqLhs
Support
If you like this article and want to see more, the best way to do that is to subscribe/follow me on here! If you are feeling gracious, you can buy me a coffee!
Top comments (12)
You can use .every() instead and return false whenever you want to
break
. Return true to keep goingInteresting! Thank you for your insight. I haven't used
every
much. Very handy!.some()
is probably even better in most cases. Just return true whenever you need tobreak
. No need to return anything when you want to keep going :)Sweet! May have to do a deep dive article into all the various loop types.
This is so helpful
Thanks! Any other topics you'd like me to tackle?
Awesome article! Minor nitpick though:
Array.prototype.forEach()
and its siblingsmap
,filter
,reduce
,reduceRight
,some
, andevery
were all actually introduced back in 2009 with ES5!I bring this up not for know-it-all points, but because it's important for browser compatibility. Most of these methods are available in browser versions going back 10+ years, which is super important and helpful if, like me, you work in an environment where you still need to maintain support for those browsers 😭
Thank you for the correction! I'm the only one who edits these things, so I appreciate any and all feedback. I'll update it to reflect accurate info!
I feel you though, I have to support old browser's too
I always remember that forEach() loop can't be break but i never know why, until now haha. Great article, thanks!
Thanks!
But why they keep all these different versions of doing the same things? That is, forEach(), every() and some().
I mean, you can if you really want: just throw sg from the callback.