The Saga of JavaScript's Most Persistent Loop
Interviewer: "Can you stop a relentless forEach
loop in JavaScript?"
That question felt like being asked to stop the Earth from spinning. There I was, in an interview room that suddenly felt as hot as a server room with broken AC.
My answer? A hesitant, "No, that's against the laws of JavaScript nature."
Cue the sound of my job prospects plummeting.
Driven by a mix of desperation and a fleeting hope of redemption, I quizzed the interviewer, "Is there some kind of secret forEach
kill-switch I'm not aware of?"
Before he could unveil the mysteries of JavaScript, I jumped in with what I knew.
Let's Spin a New Tale with Code
Here's a fun little array, just hanging out, minding its own business:
const friendlyArray = [5, 4, 3, 2, 1, 0, -1, -2, -3];
friendlyArray.forEach((number) => {
if (number <= 0) {
console.log("Stopping? Nope! Number:", number);
return; // You might think this would stop it, but nope!
}
console.log("Number:", number);
});
What does this do? It prints every number, and the return
is as effective as a screen door on a submarine.
The interviewer, still holding onto the fantasy of stopping forEach
, wasn't buying it.
Crafting a forEach
Alternative
Time to roll up the sleeves and brew a new forEach
concoction.
Array.prototype.forEachButWithStyle = function (callback) {
if (typeof callback !== 'function') {
throw new Error(`Hey, ${callback} is not a magical function!`);
}
for (let i = 0; i < this.length; i++) {
callback(this[i], i, this);
// Still no stopping - it's like a party that never ends!
}
};
Guess what? Even in this custom-built, shiny new forEachButWithStyle
, there's no emergency exit. It's like a merry-go-round that's lost its off switch.
The Plot Twist: Stopping forEach
with a throw Error
Just when you thought forEach
was the marathon runner of JavaScript, never stopping for a break, I discovered a sneaky little trick. Picture this: a forEach
loop, prancing around like it owns the place, and then suddenly, BAM! We throw an error, and it's like hitting the emergency stop button on a runaway carousel.
Here's the trick in action:
const array = [1, 2, 3, 4, 5];
try {
array.forEach((number) => {
console.log("Number:", number);
if (number === 3) {
throw new Error("Oops! Stopping the loop.");
}
});
} catch (error) {
console.log("Caught an error:", error.message);
}
In this daring feat, when we hit the number 3, we throw an error. It's like setting off a flare in the middle of our forEach
party. The loop screeches to a halt, and control is passed to the catch
block. It's not the most graceful exit, more like jumping off a moving merry-go-round, but hey, it stops the loop!
But Wait, There's a Catch!
While this method does stop the loop, it's like using a sledgehammer to crack a nut. Throwing an error just to stop a loop is like cancelling the entire circus because you don't like clowns. It's a bit dramatic and can lead to other complications in your code.
So, there you have it. The forEach
loop can indeed be stopped, but it's a bit like stopping a train with a boulder on the tracks. Effective, but not exactly recommended for everyday use.
Remember, with great power comes great responsibility. Use the throw Error
method wisely, and maybe keep it as your secret JavaScript party trick.
The Conclusion
So, is it possible to stop a forEach loop in JavaScript? Well, technically, yes, but it's a bit like stopping a carousel with a roadblock. You can abruptly halt it with a throw Error, but it's more of a last-resort trick than a graceful solution.
It's like being at a party you can't leave, and the only way out is setting off the fire alarm – effective, but you'll definitely raise some eyebrows.
Remember, the next time someone asks you about stopping forEach, you can now give them a sly smile and share this whimsically mischievous tale. While forEach might seem like the Energizer Bunny of JavaScript, we've got a little trick up our sleeves to bring it to a standstill – just use it wisely!
Top comments (73)
There actually is a way to stop a forEach loop; it merely requires throwing an Error - you may augment it with data and catch it afterwards.
Throwing errors (or exceptions in some languages) as a means of flow control is widely considered bad practice, though. Errors and exceptions should be used as such: An error is thrown if something unexpected happens. If you need to stop a for each loop, perhaps you shouldn't use a for each, but an iterator + while or a for loop instead.
You could also use an .every() loop instead and just return
false
if you want to break, but that does not answer the question how to stop a forEach loop.In addition, unlike in other languages, throwing errors is not a bad practice in JS. On the contrary, especially with Promises, it is considered a regular means of flow control.
Yes and no. A forEach loop can be broken by throwing errors, yes, but if you need to break it in your logic, you probably shouldn't use a forEach loop, because the name of it already states that you want to execute something for each element. Instead, filtering and then using a forEach is, in my opinion, more natural, because the intent is clearer. Or use an iterator with a while and break that.
I wasn't stating that throwing errors is bad, I was stating that throwing errors for flow control is bad. Errors indicate that you're not on the happy path anymore. If the errors are part of the happy path, they spark confusion. Of course promises can (and should) throw errors if something goes wrong, but if you need them to steer how something reacts to a promise, you could also resolve it with a different payload.
Errors and exceptions are always interpreted as "uh oh, something that shouldn't happen just happened." and therefore should only be used as such.
To come back to the original question: If an interviewer asked me how to break a forEach loop, I would ask them "why would I want to do that?"
Maybe the reason you need to interrupt the forEach loop is actually that you encountered an error?
And if I were the interviewer, my answer would be: "...to see if your knowledge of the language matches your CV."
Interviews are about your reaction, not about the resulting code. Would you ever want a fizz buzz in production? Me neither.
fizz buzz in production sounds like a dream 🤣
Idk how you assess candidates but the ones can question the reason of trick questions like so are usually good. They can maintain the clean source code and avoid bad practices. I’m not sure if the ones can answer tricky techniques are the good one. They can be smart or they just learned the tricks somewhere but sure they will apply them into your company’s source code whenever they have a chance.
Not sure why you think this was a trick question. It is a good example of a question requiring knowledge and professionalism.
Someone lacking knowledge will answer that it is not possible to stop for each.
Those in the know will suggest throwing errors.
Professionals will also point out that this is only a good pattern to stop on errors and otherwise suggest a different pattern.
A technical interview is meant to gauge the capabilities of the interviewed. If you know of a better way to do this, please enlightened us.
I explained the reason. I think your definition of “professionalism” is very different from the one that I know where the knowledge is not everything. Nobody can know all the programming techniques but they at least should know how to use some properly. “Requiring knowledge”, really? Your intention was wrong when you want to break “forEach”, in real life we don’t just put a regular function as a callback for you to catch the error. What if we put an
async
function, (because the iteration task requires to be awaited)? Throwing error is simply not working. I think breakingforEach
is a codesmell and it will affect the maintainability of the source code.For technical interview, there are also many aspects. For the programming languages, we usually ask about horizontal knowledge. For the essential knowledge, we can go vertically but still ask practical questions. JS and TS have lots of practical techniques to ask. Sometimes, we also ask trick questions but we assess candidates based on how they response to the questions, what they think about them and to have solutions/alternative solutions for them maybe.
But that's exactly what I wrote. Knowledge is just the step from beginner to advanced. The step towards professionalism is wisdom, where you not only factor in your knowledge about systems and languages, but also the user and your team and the context in which you develop.
You don't know the context in which the question would arise in real life and thus seem to have assumed a context that suited your opinions. But maybe the code using forEach was out of your control and you would either have to patch it (which will break easily and cause extra work) or throw an error to break the loop and handle it, while you file a ticket with the owner of the code to get another API that allows stopping the loop.
Can you throw the error before you actually await something? That would still work; otherwise, you can store the promise in a variable and do the iteration manually while calling the API with a one-element-array for each item.
I still think that if you need to break a forEach, that you probably shouldn't use a forEach, at leadt not in combination with another one. I can think of three cases that might throw errors: An invalid element in the list (for example null/undefined/a string/a negative number in a list of IDs), avalid value that produces an invalid one (for example Infinity after some multiplication, NaN, etc.) or if you need to call an external service/API for each element.
The first case can be solved by filtering the list first. If you validate the values beforehand, you can be sure that all the elements that the forEach is called for are actually valid, so no error is necessary.
The second case can be solved by using filter first and then mapping the value, or the other way around. You probably know the edge cases of that function (unit testing should take care of that), so you can filter out illegal elements beforehand.
The third case is a little trickier: Invalid elements can be filtered beforehand, thus reducing the total number of (probably expensive) HTTP calls. If the service is down, you'll likely encounter a ton of errors anyways. Ideally, you would map the values to promises, i.e. doing all calls in parallel, and have an await for all of them with a global catch. In an ideal world, you could batch-process large amounts of records with a single call anyway. Again, no forEach necessary, let alone breaking it.
I do agree, knowledge of the language is important, but what's more important, in my opinion, is to use that knowledge in the most efficient way.
EDIT: My partner (a teacher) just told me the crucial difference in opinion here: Is the interviewer testing knowledge ("can you break a forEach?") or competency ("let's see how they react to that question")? I deeply believe showing competency is more important in landing good jobs than showing knowledge, because competency grows from knowledge and, additionally, experience.
As I said already, if the forEach is in external code, your opinion could result in failure to deliver. That's also the reason why you don't want to hire developers who are too opinionated to accept pragmatic solutions that can be refactored in time, which will inevitably reduce the velocity of your team.
I already told you it is both, just that you call it "competency" and I call it "professionalism".
So how would I break the forEach, then? If it's external code, I can't alter it, except through forking/patching. And then I would restructure the code. I fail to see the scenario you're describing, what am I missing?
Regarding professionalism: You're absolutely right about that. But as mentioned, professionalism requires upfront knowledge. If you show professionalism, one can assume vast knowledge.
I should have elaborated more on the example:
I hope that clarifies the scenario.
Yeah, that does help indeed. In my opinion, in this specific case, throwing an error isn't explicitly added to only exit the forEach. The intent is different: Throwing the error exits the entire module code. If after the forEach an API call would've happened, that would not be executed either. Technically, throwing an error here does "break the forEach", but it also "jumps out of the module". If that's part of the requirements, that's fine, but if the API call afterwards should happen, throwing the error here wouldn't fulfil the requirements. I'm asking myself where the externalItems are coming from and if there's some way to filter them beforehand?
I'm very much enjoying this discussion, by the way, thank you for defending your point and challenging my arguments. It makes me think about things differently and reflect on some of my thinking patterns, which is always healthy. :)
You can actually throw stuff other than errors (and with this use case, doing that may even make it easier to differentiate an actual error from just exiting the loop - although a custom error object would likely be less frowned upon)
You're right though - it's pretty non-standard flow control, and probably not a good idea
You are right...
I have update the story 🙏
Try catch around the foreach, and we throw an exception, thats what I thought, so this article is not entirely accurate
Hi Dusan, you are absolutely correct!
Therefore I have updated the last section to accommodate for it.
It's a solution that I do not like, but it it valid. 🙏
There's no good way to stop a forEach, as its name states it will loop for each item in the iterable.
As people have said you can use try catch and throw to deal with that ungracefully (I personally would advice against doing so).
On the other side you can use a different looping tool for that job such as
while
ordo...while
, where you canbreak
the loop whenever necessary, see below:best regards
In addition to throwing an error there's at least one other way:
This loop will only print the 6 numbers non-negative numbers.
But we should not do that except when we're trying to impress interviewers :)
Modifying a list while iterating over it is its own taboo.
That's not the only thing taboo about this :)
🤣
Even if it has the appearance of it, setting length=0 does not break forEach. It will still check for the existence of the indices for your negative numbers. See this jsfiddle.
process.exit() :S
WOW! that's true, but I don't know if I'd go that far haha
This is not js... this node implement
this exists the whole program, not just the loop
but it stops the loop! dude it's not that serious :)
I know, but usually when you early terminate a loop it's because you've encountered data that you want to be used elsewhere, ending the program doesn't allow that, making it pretty impractical. The error solution is bad enough, so I get that this isn't serious.
Thank you for sharing this story. I wondered for a while when first reading the question.
We have
continue
andbreak
infor
loop. It also obeysawait
before moving to next element.forEach
is like a rebel.That's probably what it does best. Being an unstoppable rebel.
Output:
👀
Even if it has the appearance of it, setting length=0 does not break forEach. It will still check for the existence of the remaining indices. See this jsfiddle.
why not just turn off your computer?
🤣 always an option
No need, just call
alert()
in the middle of the loop and the loop will stop until the modal is closed :)Remember that plain old for-loop is always an option.
I guess you can throw an error, but you shouldn't. I suppose it's about semantics, so, you should either use a for-loop or rather filter first, something like:
The answer is no, not without throwing an error. Consider using a different method or approach if you want the iteration to stop, perhaps
find
orsome
methods, or a traditional loop.