DEV Community

Cover image for Why can't you break out of the forEach loop?
mayankav
mayankav

Posted on

Why can't you break out of the forEach loop?

This is one of the many things I keep forgetting every now and then. Other things like taking out clothes from the washing machine, watering my plants, okay but this post is not about me being forgetful. I am sure lot many of you reading this post do know this but still its not until you try to break out of the "forEach" loop that you realize you made the same mistake once again. This post is just a reminder for most of us and maybe a short clarification for the rest of us.

Forgetful

Moving ahead with an example because examples are the quickest way to explain stuff. Lets say we have an array of flowers and we want to collect all the flowers unto my favorite "Tulip" starting from the first one. Yes, please dont make it complicated by throwing in scenarios like what if there are multiple "Tulips". We have an array of flowers, where each flower appears once like so:

const flowers = ["Rose", "Lily", "Marigold", "Jasmine", "Tulip", "Lotus", "Orchid", "Daffodil", "Sunflower", "Poppy"];

How would you do it? If I were you I'd rush to get the "forEach" loop down first but not this time. Lets use the conventional for loop and get this done first. Silly but helpful. Easy said, easy done! Then we shall also try that out with my favorite "forEach" loop. Take a look at the following snippets.

const flowers = ["Rose", "Lily", "Marigold", "Jasmine", "Tulip", "Lotus", "Orchid", "Daffodil", "Sunflower", "Poppy"]; let vase = []; //collect all flowers unto "Tulip" for(let i=0; i < flowers.length; i++) { vase.push(flowers[i]); if(flowers[i]==="Tulip") break; } console.log(vase); // ["Rose","Lily","Marigold","Jasmine","Tulip"]

const flowers = ["Rose", "Lily", "Marigold", "Jasmine", "Tulip", "Lotus", "Orchid", "Daffodil", "Sunflower", "Poppy"]; let vase = []; //collect all flowers unto "Tulip" flowers.forEach(flower => { vase.push(flower); if(flower==="Tulip") { break; //Illegal break statement } });

Had you tried that on your own, you'd have come across this error that says "Illegal break statement". You can't break out of the forEach loop after you're done collecting "Tulip". You may have already figured out but don't worry if you haven't and let me tell you how does the forEach loop differ from all the conventional loops. "forEach" more than being a loop, is a function call and you know it right from the syntax. This function takes another callback function and mind it, the callback function has to be a synchronous function. The synchronous callback function essentially is then called in interation on the array on which you did call the "forEach" loop. I understand, that this may not sound very intuitive. So, let me try explaining this with a demo polyfill of the "forEach" function. You can easily make it go crazy throwing errors but for a quick example, it should suffice. For complete implementation you can see this.

// forEach polyfill Array.prototype.customForEach = function(callback) { const arr = this; // internal loop for(let i=0; i<arr.length; i++) { const x = arr[i]; callback.call(arr, x); // only if we could add a break here, but we can't reach this place } } const callback = function(x) { console.log(x); // break; // Illegal break statement } // using custom forEach const flowers = ["Rose", "Lily", "Marigold", "Jasmine", "Tulip", "Lotus", "Orchid", "Daffodil", "Sunflower", "Poppy"]; flowers.customForEach(callback);

Now when you look at the poyfill (customForEach), you'll be able to comprehend the situation much clear. You'd also agree how placing a "break" statement inside a function (named callback) is inappropriate. Read what MDN docs say. Even if you assumed that something like a "break" statement was valid inside a function (which indeed isn't the case), you'd see that it would not be able to break out of the loop, since there's no way you can add a "break" statement inside the internal loop. Why? because you simply cannot access it from outside.

Accomplishing the task we took as an example here for this post, is not a tough job. There are numerous ways this can be done but apart from this example, for your use-case checkout the alternatives available at your disposal. Again, this is not an exhasutive list but then the purpose of this post is served and anything beyond this would only stretch the content unnecessarily I believe. Also since I have now written this post, I think I won't repeat this mistake or will I?

Do not forget


Originally Posted Here -

https://mayankav.webflow.io/blog/you-cant-break-the-foreach-loop

Discussion (18)

Collapse
jonrandy profile image
Jon Randy • Edited on

You can throw an exception. Not the prettiest solution, but it works...

const BreakException = {}
try {
  [1, 2, 3].forEach(function(el) {
    console.log(el)
    if (el === 2) throw BreakException
  });
} catch (e) {
  if (e !== BreakException) throw e
}
Enter fullscreen mode Exit fullscreen mode

Another way is to just use some and return a truthy from your function if you want to break out:

[1, 2, 3].every(function(el) {
   console.log(el)
   if (el===2) return true
})
Enter fullscreen mode Exit fullscreen mode
Collapse
lionelrowe profile image
lionel-rowe

Why use an unreadable, hacky solution when for... of exists?

Collapse
jonrandy profile image
Jon Randy

Many ways to skin a cat, couldn't be bothered to list them all

Thread Thread
lionelrowe profile image
lionel-rowe

What I'm saying is, if you care about code quality, why not use the best tool for the job?

Thread Thread
jonrandy profile image
Jon Randy • Edited on

I care about code. I care about, and am interested in the many permutations that can achieve the same result - including all the weird and wonderful ways. To me, that's the beauty of code, and is why I love it. Correctness, readability, and quality are purely subjective. I personally cannot think of anything worse than blindly following the dogmatic methodologies promoted as the best, or right way to do things - without playing and enjoying exploring alternatives.

Writing code, from my perspective, is more like art crossed with science than engineering. It's a very personal thing - like writing a novel, or poetry, or painting a picture. There can very definitely be beauty in it.

This approach does inevitably clash with others who follow a more traditional software engineering route, but it has served me well over the 38 years I've been writing code.

Thread Thread
lionelrowe profile image
lionel-rowe

I sympathize with that approach for sure, but I see it as similar to writing prose, which is certainly a creative endeavor. Sure, you could write an essay caPitALiZIng RAndOM leTtErs or spelıng uerds uıth ior oun ınventıd orthogr'fy, but who would want to read it, much less be your co-writer on it? There are conventions for a reason, and you should have an even better reason if you want to break them. That doesn't mean for a second that you can never break them — it just means that, for example, you should typically avoid adding complexity where you don't gain something (performance, flexibility, etc) of at least equal value in return.

Thread Thread
jonrandy profile image
Jon Randy • Edited on

Have you read Cormac McCarthy's "The Road"? That won a Pullitzer prize, and would probably get you a fail from most school English teachers if you were to submit it, or something similar for a writing assignment.

Most of the best art is made by breaking or testing the rules to their limits. I much prefer to read code that is 'hard to reason about' than code that reads like a class reader for five year olds - it gives me pause to think and exercise my brain, and maybe lend new perspectives on ways to use code.

I think we'll have to agree to disagree

Thread Thread
lionelrowe profile image
lionel-rowe

I think we'll have to agree to disagree

That's fair.

Thread Thread
mayankav profile image
mayankav Author

Much appreciation to you both for the exchange of thoughts. I think playing around with the code and conventions is as important as keeping a shared repository clean and conventional. I like how Jon added his perspective to the post and I also agree with Lionel about readability when it comes to code in production. Cheers 🥂

Collapse
mayankav profile image
mayankav Author

@jonrandy Indeed Jon! This post doesn't cover the possible solutions for a reason. I found it easier to throw in an MDN link which covers some alternatives.

Collapse
shuckster profile image
Conan

Nice explanation as to why break won’t work by writing your own version of forEach.

I know you linked to some alternatives already and this is probably familiar, but here’s an example I popped together using some / every.

Collapse
mayankav profile image
mayankav Author

@shuckster I like what you did there on zansh.in

Collapse
przemek profile image
Przemyslaw Michalak • Edited on

You can't break from forEach because that's the purpose of forEach loop. In JS you have reduce, map, while, do while, for, filter and god knows how many other types of loops. The reason for forEach to exist is to NOT allow you to break from it! It's like complaining that the sit belt in the car have a functionality of saving your life in the accident, well... that's the purpose, not a flaw.

Collapse
mayankav profile image
mayankav Author • Edited on

Hey man! appreciate the analogy. There can indeed be many more reasons why you'd go with a forEach loop (keeping variables scoped, clean abstraction etc..). Tbh, I dont really know the motivation(s) behind the creation of forEach in the first place but that hardly matters. This is just me pointing out a simple mistake that most of us make. I do this over and over even when I know the stuff.

Collapse
ben profile image
Ben Halpern

Ha, great post

Collapse
haiderali2162 profile image
haiderali2162

Good job💗💗💗💗💗
Thank you....

Collapse
kuya1284 profile image
kuya1284

I think defining your own custom forEach loop is redundant. You can use what's already available, like using Array.some().

If you need to simulate "continue" in a forEach loop, use "return".

Collapse
mdqayyumshareef profile image
Qayyum Shareef

I think we can use 'return' to exit for each loop.