I came across a use of .forEach[^mdnForEach] which looked like this:
```ts
const areAllItemsValid = (items: MyItem[]): boolean => {
let count = 0
items.forEach((item) => {
if(!item.isValid()) {
return false;
}
count = count + 1;
})
if (count > 3 || count === 0) {
// Too many valid things
return false;
}
// Everything must be valid and good
return true;
}
```
This hides a very tricky to spot scope issue, `items.forEach` is called for every item in the list, regardless of any previous call. At the same time, the result of `forEach` is entirely ignored by the runtime.
If this function were to be called with an item that failed the `.isValid()` call, the arrow function _within_ the .forEach would return false. **But that doesn't mean that the invocation of `areAllItemsValid` would return false.** Instead, that false-y return is just ignored by the runtime, never to be seen again.
In my experience, almost every call of `.forEach` really meant to be a `.map`[^mdnMap]. The developer was aiming to perform some action on every item in the set and collate the responses into a new set.
```ts
let mangledItems = []
items.forEach((item) => {
mangledItems.push(mangle(item))
})
// vs
const mangledItems = items.map(mangle)
```
The other cases of `.forEach` were often a test to see if "everything" in the set passed some condition or "at least one of" the items in a set passed some condition, or inversely none of them passed. In this case, the functions `Array.prototype.every`[^mdnEvery] and `Array.prototype.some`[^mdnSome] function similar to map but return a single boolean value based on invocation with each item.
```ts
// Every item in the items set passed the isEven check
const allEven = items.every(isEven)
// At least one item in the items set passed the didSucceed check
const atLeastOneSuccess = items.some(didSucceed)
```
Getting "comfortable" with `.forEach` also opens up the developer for making a mistake like the following:
```ts
const results = await items.forEach(async (item) => {
return processData(item)
})
```
> `forEach()` expects a synchronous function — it does not wait for promises
The `forEach` function may feel like sexy functional programming, but do not be fooled! Fall back to normal imperative programming and use a `for` loop or a `.map` instead.
Yes, yes, I hear you yelling "but for loops have `i` and `i` is a very freshman thing to do!" Most languages have a simplified for/foreach syntax which will do all the heavy lifting for you...
```ts
// Yes it's imperative and the nerds may make fun of you
// but its clear, and they'll make less fun of you than if
// you implemented .forEach wrong!
for (const item of items) {
await processData(item)
}
```
[^mdnForEach]: [MDN Array forEach](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/forEach)
[^mdnMap]: [MDN Array map](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map)
[^mdnEvery]: [MDN Array every](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/every)
[^mdnSome]: [MDN Array some](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/some)