Command Palette

Search for a command to run...

Hectal
PHASE 5Beginner ~18 min· topic 7 of 10

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 break once you've found what you need.
entries()
An array method that gives [index, value] pairs, for use with for...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.

app.jswhole filejavascript
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.

app.jswhole filejavascript
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.

app.jswhole filejavascript
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.

Choose by what you needdiagram
Rendering diagram…

Try it yourself

  1. 1

    Skip, don't stop

    In the second example, change the for...of loop to print every number except those over 20, using continue. Predict the lines first.

  2. 2

    Number a list

    In the third example, use entries() in the for...of loop so each <li> reads 1. a, 2. b.

  3. 3

    Try to break

    In the second example, put break; inside the forEach callback instead of return;. Predict what the console says before you run it.

Code & diagrams

Four ways to visit every item ES2015+ New tab

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
Early exit: for...of can stop, forEach can't New tab

`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
Why not for...in on arrays New tab

`for...in` gives property names as strings, and includes the extra property. `for...of` gives exactly the array's values.

Preview

Press Run to see the page.

Console

Nothing logged yet.

Expected console output

in: 0 string
in: 1 string
in: extra string
of: a
of: b

Break 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; });.

terminal
$ Console
── what you'll see ──
Uncaught SyntaxError: Illegal break statement

Break #2

await inside forEach

Write items.forEach(async (item) => { await save(item); }); console.log("all saved");.

terminal
$ Console
── what you'll see ──
"all saved" appears before any item has finished saving.

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...of uses the iterator protocol: it calls arr[Symbol.iterator]() and then .next() until done is 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...of reads the live array, so pushing inside the loop can make it run forever; forEach fixes the range at the start but still sees changed values. Removing items while looping forwards skips the next item; loop backwards or filter instead.

  • ▸

    forEach skips holes; for...of and the classic for visit them as undefined.

  • ▸

    forEach takes an optional second argument, thisArg, used as this inside a non-arrow callback. With arrow functions (Phase 3) it isn't needed.

Remember this

  1. 1

    The **classic for loop** (Phase 2), for (let i = 0; i < arr.length; i++), gives you full control: the index, the step size, going backwards, skipping items, break and continue. It's the most flexible and the most error-prone (off-by-one mistakes).

  2. 2

    **for...of (ES2015) loops over the values** of any iterable: for (const fruit of fruits) { ... }. It's short, reads like English, and supports break, continue, return (inside a function) and await (inside an async function). Need the index too? Loop over arr.entries(), which gives [index, value] pairs: for (const [i, fruit] of fruits.entries()).

  3. 3

    **forEach(fn)** calls fn(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: break is a syntax error inside the callback, and return only ends that one call, like continue.

  4. 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, use forEach or for...of instead.

  5. 5

    Early exit: to stop at the first match, use for...of with break, or a method that stops by itself: find, findIndex, some, every (Topic 5.3). Async: forEach doesn't wait for async callbacks; it starts them all and moves on. Use for...of with await to go one at a time, or Promise.all(arr.map(...)) to run them together (Phase 11 covers promises).

  6. 6

    Avoid **for...in on arrays. It loops over property names** (as strings, "0", "1"), includes any extra properties someone added, and isn't meant for ordered lists. for...in is for objects (Phase 6); for...of is for arrays.

Explain it without notes

01

What is the difference between forEach and map?

02

How do you stop looping through an array early?

03

Why shouldn't you use forEach with async callbacks when order or completion matters?

04

Why is for...in a poor choice for arrays?

Practice

01

Print each name in ["Asha", "Ravi", "Meera"] as 1. Asha, 2. Ravi, 3. Meera using for...of and entries().

02

Loop over [3, 7, -2, 9] and stop at the first negative number, printing Negative at index 2.

Trade-offs

  • ↔

    forEach reads neatly with a named callback but can't break or await; for...of handles both at the cost of a few more lines.

  • ↔

    The classic for loop is the most flexible (custom steps, backwards) but easiest to get wrong; prefer for...of unless you need that control.

Done when you can

  • I can loop over an array with for, for...of, entries() and forEach.

  • I know forEach returns undefined and can't be stopped with break.

  • I use for...of with break, or find/some, for early exit.

  • I don't use forEach with await.

  • I use for...of, not for...in, for arrays.