Array.prototype.forEach()

The one iteration method that produces nothing. Its two real limitations — no return value and no way to break — are exactly why for...of often reads better.

Array methodES5 (2009)Live demo
Common call
items.forEach(x => console.log(x))
Returns
undefined — always
Replaces
a plain for loop when you only want side effects
Watch out
you cannot break; return only skips the current element
array.forEach(callback[, thisArg])
→ undefined

Demo

Live evaluation
Try:
Inputs
itemsnumber[]numbers, comma separated
Output
[1, 2, 3].forEach(x => x)
undefined

Every case returns undefined, and that is the whole lesson — no matter what the callback does or what it returns, forEach produces nothing. The work happens entirely through side effects. This is why `const result = items.forEach(...)` is always a bug, and why you cannot chain .filter() or .join() onto the end of a forEach.

Parameters

NameTypeRequiredDescription
callbackFunctionyesCalled as callback(element, index, array). Its return value is ignored entirely.
thisArganyno (undefined)Value of `this` inside the callback. Ignored for arrow functions.

Return value

undefined — Always undefined — whatever the callback returns is discarded. Nothing can be chained onto it.

Common patterns

Pure side effects
Logging, sending, writing — where no value comes back.
items.forEach(x => console.log(x));
Prefer for...of when you might break
forEach cannot stop early; a real loop can.
for (const x of items) {
  if (found(x)) break;
}
Use map when you want a result
If a value comes out, forEach is the wrong method.
const doubled = items.map(x => x * 2);

Examples

1. Returns undefined
[1, 2, 3].forEach(x => x * 2)
Returns
undefined
2. Side effect works
const out = []; [1, 2].forEach(x => out.push(x)); out
Returns
[1, 2]
3. return is continue
const out = []; [1, 2, 3].forEach(x => { if (x === 2) return; out.push(x); }); out
Returns
[1, 3]
4. Cannot chain
[1, 2].forEach(x => x).length
Returns
TypeError: Cannot read properties of undefined
5. Skips holes
let n = 0; [1, , 3].forEach(() => n++); n
Returns
2 // the hole is skipped
6. Empty array
[].forEach(x => x)
Returns
undefined

Pitfalls

1. You cannot break out of it
There is no early exit. return inside the callback behaves like continue — it ends that one call, not the loop. Iteration always runs to the end, however much work is left.
return is continue
items.forEach(x => { if (found) return; check(x); });
keeps iterating to the end
Use for...of
for (const x of items) { if (found) break; check(x); }
stops immediately
2. It returns undefined, so nothing chains
Assigning the result or chaining another method onto it fails. Coming from map, the mistake is easy — the two have identical signatures and completely different outputs.
Nothing comes back
const doubled = items.forEach(x => x * 2);
undefined
Use map
const doubled = items.map(x => x * 2);
[2, 4, 6]
3. It does not await async callbacks
An async callback returns a promise that forEach discards, so the loop finishes long before the work does. The surrounding function carries on with nothing done yet.
Fires and forgets
items.forEach(async x => { await save(x); });
done();
done() runs before any save finishes
Use for...of
for (const x of items) { await save(x); }
done();
awaits each one
4. It skips holes in sparse arrays
Empty slots are not visited at all, so the callback runs fewer times than the length suggests. new Array(3).forEach(...) never calls the callback once.
Never runs
let n = 0;
new Array(3).forEach(() => n++);
n
0
Fill first
new Array(3).fill(0).forEach(() => n++);
3

When to use

Use it
  • Side effects only — logging, sending, mutating something external
  • When the array is small and you will never need to stop early
Reach for something else
  • You want a value back → map, filter or reduce
  • You might need to stop early → for...of with break
  • The callback is async → for...of with await
  • The array is sparse → fill it first, or use a for loop

Notes

Complexity
O(n) — always runs to the end; there is no short circuit
Return
Always undefined; the array is not modified by forEach itself
CPython impl
V8: Builtins-array-foreach.tq
Memory
No allocation
Thread-safe
Single-threaded; mutating the source inside the callback is undefined behaviour for unvisited indices

FAQ

You cannot. return acts as continue, and there is no break. If you need early exit, use for...of with break — or some/every, which short-circuit by design, though they read as questions rather than loops.

for (const x of items) {
  if (done) break;
}

History

ES5
forEach standardised in 2009 with the other iteration methods.
ES2015
for...of arrived and covers most of what forEach was used for, with fewer limitations.