Topic 5.7
forEach and Iteration Choices
In one line
You can walk through an array with a classic for loop, for...of, forEach or map; forEach runs a function for each item and returns nothing, can't be stopped early, and is the wrong choice when you need break, a result array, or await.
Think of it like this
Checking every ticket at a cinema door. You can count the seats yourself (seat 0, seat 1, …), that's the classic for loop. You can say "next person, please" until the line is empty: for...of. You can hand the job to an usher with instructions ("check each ticket"): forEach; once the usher starts, they check every single person, and you can't call them off halfway. And if you need a new list ("write down each person's seat number"), that's map.
Words you'll meet
New words in this topic, in plain English. Come back here whenever one feels fuzzy.
- Iterate
- Go through the items of a collection one by one.
- for...of
- A loop that gives you each value of an array (or other iterable) in turn.
- forEach
- An array method that runs a function once for each item and returns nothing.
- Side effect
- Something a function does besides returning a value: changing the page, logging, changing a variable outside it.
- Early exit
- Stopping a loop before the end, for example with
breakonce you've found what you need. - entries()
- An array method that gives
[index, value]pairs, for use withfor...of. - for...in
- A loop over an object's property names; not suited to arrays.
Step by step
01The classic for loop
You manage the index yourself. Use it when you need unusual steps, like every second item or going backwards.
for (let i = arr.length - 1; i >= 0; i--) {
console.log(arr[i]); // backwards
}02for...of for values
The everyday loop. Add .entries() when you also want the index.
for (const [i, name] of names.entries()) {
console.log(i + 1, name);
}03forEach for a ready-made action
Good when each item gets the same action and you'll never stop early.
document.querySelectorAll("button").forEach((btn) => {
btn.addEventListener("click", onClick);
});04Choose by what you need
Ask: do I need a new array? To stop early? To await? The answers pick the tool.
Try it yourself
- 1
Skip, don't stop
In the second example, change the
for...ofloop to print every number except those over 20, usingcontinue. Predict the lines first. - 2
Number a list
In the third example, use
entries()in thefor...ofloop so each<li>reads1. a,2. b. - 3
Try to break
In the second example, put
break;inside theforEachcallback instead ofreturn;. Predict what the console says before you run it.
Code & diagrams
All four visit both items in order. The last line shows that `forEach` returns `undefined`.
Console
(nothing was printed)Expected console output
for 0 red
for 1 green
for...of red
for...of green
entries 0 red
entries 1 green
forEach 0 red
forEach 1 green
undefined`return` inside `forEach` only ended that one call; all 6 items were still visited. `find` stops by itself.
Console
(nothing was printed)Expected console output
for...of found 15 after 3
forEach visited 6
find: 15`for...in` gives property names as strings, and includes the extra property. `for...of` gives exactly the array's values.
Preview
Console
Nothing logged yet.Expected console output
in: 0 string
in: 1 string
in: extra string
of: a
of: bBreak it on purpose
Errors are the best teachers. Make each change, read the error, guess what went wrong, then reveal the answer.
Break #1
break inside forEach
Write nums.forEach((n) => { if (n > 10) break; });.
Break #2
await inside forEach
Write items.forEach(async (item) => { await save(item); }); console.log("all saved");.
Myth vs fact
Myth
return inside forEach stops the loop.
Fact
It only ends the current callback call, like continue. The next item is still processed.
Myth
forEach is faster than a for loop.
Fact
In modern engines they're close; plain for and for...of are usually at least as fast. Choose by readability and what you need (early exit, await).
Myth
for...in and for...of are the same.
Fact
for...in loops over property names (strings) of any object, including added and inherited enumerable properties. for...of loops over the values of an iterable. Use for...of for arrays.
Pro corner
Extra depth for experienced readers. New to this? Skip it for now and come back later.
- ▸
for...ofuses the iterator protocol: it callsarr[Symbol.iterator]()and then.next()untildoneis true. That's why it works on strings, Maps, Sets, NodeLists and generators too. You'll build your own iterables in a later phase. - ▸
Changing an array while looping over it is risky.
for...ofreads the live array, so pushing inside the loop can make it run forever;forEachfixes the range at the start but still sees changed values. Removing items while looping forwards skips the next item; loop backwards orfilterinstead. - ▸
forEachskips holes;for...ofand the classicforvisit them asundefined. - ▸
forEachtakes an optional second argument,thisArg, used asthisinside a non-arrow callback. With arrow functions (Phase 3) it isn't needed.
Remember this
- 1
The **classic
forloop** (Phase 2),for (let i = 0; i < arr.length; i++), gives you full control: the index, the step size, going backwards, skipping items,breakandcontinue. It's the most flexible and the most error-prone (off-by-one mistakes). - 2
**
for...of(ES2015) loops over the values** of any iterable:for (const fruit of fruits) { ... }. It's short, reads like English, and supportsbreak,continue,return(inside a function) andawait(inside anasyncfunction). Need the index too? Loop overarr.entries(), which gives[index, value]pairs:for (const [i, fruit] of fruits.entries()). - 3
**
forEach(fn)** callsfn(element, index, array)once for every element and returns **undefined**. It's a neat fit for "do something with each item" when you have a function ready:buttons.forEach(addClickHandler). But you can't stop it early:breakis a syntax error inside the callback, andreturnonly ends that one call, likecontinue. - 4
**
map** is not a loop for side effects; it's for building a new array (Topic 5.4). If you're not using its result, useforEachorfor...ofinstead. - 5
Early exit: to stop at the first match, use
for...ofwithbreak, or a method that stops by itself:find,findIndex,some,every(Topic 5.3). Async:forEachdoesn't wait forasynccallbacks; it starts them all and moves on. Usefor...ofwithawaitto go one at a time, orPromise.all(arr.map(...))to run them together (Phase 11 covers promises). - 6
Avoid **
for...inon arrays. It loops over property names** (as strings,"0","1"), includes any extra properties someone added, and isn't meant for ordered lists.for...inis for objects (Phase 6);for...ofis for arrays.
Explain it without notes
What is the difference between forEach and map?
How do you stop looping through an array early?
Why shouldn't you use forEach with async callbacks when order or completion matters?
Why is for...in a poor choice for arrays?
Practice
Print each name in ["Asha", "Ravi", "Meera"] as 1. Asha, 2. Ravi, 3. Meera using for...of and entries().
Loop over [3, 7, -2, 9] and stop at the first negative number, printing Negative at index 2.
Trade-offs
- ↔
forEachreads neatly with a named callback but can't break or await;for...ofhandles both at the cost of a few more lines. - ↔
The classic
forloop is the most flexible (custom steps, backwards) but easiest to get wrong; preferfor...ofunless you need that control.
Done when you can
I can loop over an array with
for,for...of,entries()andforEach.I know
forEachreturnsundefinedand can't be stopped withbreak.I use
for...ofwithbreak, orfind/some, for early exit.I don't use
forEachwithawait.I use
for...of, notfor...in, for arrays.