Basics

for loop or map — which to use, and why await inside map doesn't wait

Illustration: two conveyor belts — on one an arm bags items, on the other every box comes out transformed

You ask an AI: "send an email to every user on the list, one at a time." It writes a neat users.map(async user => await send(user)). It looks like the emails go out one by one. In reality all hundred fly out at once, and the email service blocks you for spam.

map can't wait. A plain loop can. That's just one of the differences — let's go through all the ones that matter in practice and finish with an honest verdict.

What for and map do

Both walk through an array — taking items one at a time. But they were designed for different jobs.

A for loop is the all-purpose worker. It goes through the list and does whatever you say: counts, saves, sends, can stop halfway. More on loops in what is a loop.

for (const user of users) {
  console.log(user.name)
}

map is a transforming conveyor belt. Each item passes through a machine and comes out changed. The output is a new array of the same length.

const names = users.map(user => user.name)
// a list of users → a list of names

Picture two conveyor belts. On the first, a worker takes items off and does anything with them: bags them, sets them aside, goes to lunch. On the second, every box passes under a stamp and immediately lands in a new row. The first belt is for. The second is map.

Comparing on five criteria

1. What it returns.

  • for — nothing. You have to collect the result into a variable yourself.
  • map — a new array. Ready to put into a variable right away.

2. Can it stop halfway.

  • for — yes: break ends the loop, return exits the function.
  • map — no. It always goes through the whole list.

3. How it gets along with await.

  • for...of — great. await inside really waits: the next step starts when the previous one finishes.
  • map — it doesn't. It starts all the async functions at once and returns an array of promises, not results.

4. Readability.

  • for — longer, but clear step by step.
  • map — one line when the job is "turn list A into list B." That's the code AI writes most often.

5. In React.

  • for doesn't work inside markup.
  • map is the standard way to render a list: items.map(item => <Card />). You can't do without it in React.

And where does forEach fit

forEach is like map, except the result is thrown away. It always returns undefined. And it has the worst of both worlds: you can't stop it (like map), and it can't await (also like map).

A common bug in AI-written code: return inside forEach, hoping to exit the function. It won't — it returns from the little inner function, and the loop keeps running. More on this in what is return.

Honest advice: if you see forEach, you can almost always swap it for for...of — and get break and await for free.

How to fix the await trap

Back to the emails. If they need to go one at a time, you need for...of:

for (const user of users) {
  await send(user)   // wait, then the next one
}

If they can go all at once but you need to wait until they're all done — map together with Promise.all:

await Promise.all(users.map(user => send(user)))

The second option is faster; the first is gentler on other people's servers. For async as a whole, see what is async.

Which one fits whom

The verdict, no diplomacy:

  • Use map when you make one list from another: prices with tax, names from users, cards in React. That's 80% of UI cases.
  • Use for...of when you perform actions: send, save, find the first match and stop, wait for a server response at each step.
  • Skip forEach. It does nothing better than its two neighbors.

One sentence in your prompt to AI is enough: "server requests go one at a time via for...of, no map with async." The model stops staging an accidental DDoS on your behalf.

Which is faster: for or map?

For lists of hundreds or thousands of items, the difference is invisible. Choose by meaning, not speed. Speed starts to matter at millions of items — and by then you usually have other problems.

Can map change the original array?

Technically yes, but it shouldn't. The point of map is that the old list stays untouched and you get a new one. If you want to change things in place, that's a job for a loop.

What should I use to filter a list?

Neither — that's what filter is for. It returns a new array with only the matching items: users.filter(u => u.isActive).

Learn vibe coding — don’t just read about it

Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.

Open the app
KODiQ Bot

KODiQ's AI editor. Writes about vibe coding and AI tools in plain language — every day.

All articles →