The Saga of JavaScript's Most Persistent Loop
Interviewer: "Can you stop a relentless forEach loop in JavaScript?"
That question felt like being ...
For further actions, you may consider blocking this person and/or reporting abuse
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.Thanks for bringing it up! =)
That's right, thank you.
and what about this one
Not really stopping the forEach. Just early return of all after the 3. If Array has 1 billion elements it would still go thru them.
I prefer using every, as that means you have to return false to break. It makes for a more understandable pattern.
Wow! I thought I was a good storyteller. You are brilliant. 🤗 That is an incredibly beautiful read.
I am not an expert of JS (used every now and then when the need arises), so sorry for the naive question... From this I guess that there is no
break
statement in JS or that, at least, it does not work withforEach
. Why? As I understand thatforEach
means loop over all members, I imagine you can easily imagine situations where a premature loop ending is legit.It is a red flag question. forEach is not a loop and using exceptions to interrupt it is a bad practice.
err.. I mean for the part where we create our own array method we could say that if the callback returned some sign to keep going or to stop then it should be possible:
we could even wrap the callback in a try block as well to semi-gracefully not have to handle the error.
Just for fun, I made a custom-build that works by changing any
return
into the function to a value that triggers thebreak
in afor
loop.Output:
forEach is the most inferior for loop.
Lev, but it is easy to read and understand.
Much more readable then for loop to my option.
Sometimes it's all about readability
How is
less readable than
A simple for loop, or for of loop are extremely easy to read without all the downsides of forEach. I see absolutely no reason to use it.
Use
splice
to delete the remaining items and jump out of the loop directly.The REAL question here is:
What is the point of this interview question?
Seriously...this does not apply in any real world scenario since if you are looping like that 99% of the time you're looping through some interface elements that are limited in number and you don't care if it loops through all of them even if you don't need it to.
And no it won't impact performance unless you're writing bad code that loops through hundreds of elements needlessly, that's a different can of soup.
Oh no!, Are you the 1% doing something else?! omg omg! ...Use a for loop. The end.
No one needed to know the answer to this question. It adds zero value to an interview because knowing this vague bit of trivia does not in any way tell an interviewer if you know how to code.
I think you have a point here about what is the point of the interview question. I think we get caught up in trying to prove that we know how to code that we forget that there are other aspects of "getting hired".
As I read through these comments I can see the difference in how well some can work with others and how some might brute force their opinions and how some are creative in how they solve for solutions.
With that said, I do think it's a great question to know the answer to as some may still be learning and not know when to use or not use a particular technique.
"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."
developer.mozilla.org/en-US/docs/W...
They aren't asking it cause there is an answer, they are asking to see if you try to solve it. Also before answering, there are some basic questions to ask like: what is the forEach doing, why is it relentless, and why must it be a forEach if it is 'relentless' that implies the use case is, perhaps, an edge case of some sort. The point isn't to be clever and House MD an answer but to explore and to bring the interviewer along for the ride. Also I think the you could have a stopping mechanism in the function iterated over the elements with the forEach loop, maybe a return of some sort in the function body? Idk just spit balling.
This is why Array.prototype.some exists, if you need to stop you can return truthy value and it's ended.
If someone asked me that, I would say no.
If they said "actually, you can just throw inside to stop it"
Then I would say there is a better way to do this instead of using
forEach
.`array.forEach(function(element) {
// Your code here
console.log(element);
if (condition {
break;
}
});`
Use it like this :)
BTW, throwing an exception is how python implements its for loops (you can manually raise a
StopIteration
to break out of the loop)debugger;
Love your writing style. "Cracking a nut with a sledgehammer" I literally pictured this in my mind :D ( And all the other references)
Thank you for sharing
I do not JS I fear 😨
Do not use exceptions as control flow
Thanks for sharing !!
forEach is for each. you can use find if you want to stop after finding element matching criteria
Can't stop using return
Although I disagree with the answer, it is a legitimate one.