Explore the future of Web Scraping. Request a free invite to ScrapeCon 2024

Why you can't break a forEach loop in JavaScript

No break in ForEach()

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.

image

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.

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:

  1. The item in the array

  2. The index of the item

  3. 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
    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.

image

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

image

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.




Continue Learning