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

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:breakends the loop,returnexits the function.map— no. It always goes through the whole list.
3. How it gets along with await.
for...of— great.awaitinside 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.
fordoesn't work inside markup.mapis 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
mapwhen you make one list from another: prices with tax, names from users, cards in React. That's 80% of UI cases. - Use
for...ofwhen 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).
Short story-lessons, an agent simulator and daily practice — in our mobile app. Free.





