Цикл for или map — что выбрать, и почему await внутри map не ждёт

Ты просишь ИИ: «отправь письмо каждому пользователю из списка, по очереди». Он пишет аккуратный users.map(async user => await send(user)). Выглядит так, будто письма уходят по одному. На деле все сто улетают разом, и почтовый сервис блокирует тебя за спам.
map не умеет ждать. А обычный цикл умеет. Это лишь одно из отличий — разберём все, что важны на практике, и в конце вынесем честный вердикт.
Что делают for и map
Оба перебирают массив — берут элементы по одному. Но задумывались они для разного.
Цикл for — универсальный рабочий. Проходит по списку и делает что скажешь: считает, сохраняет, отправляет, может остановиться на середине. Подробно о циклах — в статье что такое цикл.
for (const user of users) {
console.log(user.name)
}
map — конвейер-преобразователь. Каждый элемент проходит через станок и выходит изменённым. На выходе — новый массив той же длины.
const names = users.map(user => user.name)
// из списка пользователей → список имён
Представь две ленты конвейера. На первой рабочий снимает вещи и делает с ними что угодно: складывает в мешок, откладывает, уходит на обед. На второй каждая коробка проходит штамп и тут же встаёт в новый ряд. Первая лента — for. Вторая — map.
Сравнение по пяти критериям
1. Что возвращает.
for— ничего. Результат надо самому складывать в переменную.map— новый массив. Готовый, сразу можно класть в переменную.
2. Можно ли остановиться на середине.
for— да:breakпрерывает цикл,returnвыходит из функции.map— нет. Он всегда пройдёт весь список до конца.
3. Как дружит с await.
for...of— отлично.awaitвнутри действительно ждёт: следующий шаг начнётся, когда закончится предыдущий.map— никак. Он запускает все асинхронные функции сразу и возвращает массив обещаний, а не результатов.
4. Читаемость.
for— длиннее, зато понятно пошагово.map— одна строчка, если задача «из списка A сделать список B». Такой код ИИ пишет чаще всего.
5. В React.
forвнутри разметки не работает.map— стандартный способ нарисовать список:items.map(item => <Card />). Без него в React не обойтись.
А куда делся forEach
forEach — это как map, только результат выбрасывается. Он всегда возвращает undefined. И у него худшее от обоих миров: остановить нельзя (как у map), ждать await не умеет (тоже как у map).
Частый баг в коде от ИИ: return внутри forEach в надежде выйти из функции. Не выйдет — он вернёт из маленькой внутренней функции, а цикл побежит дальше. Подробнее про это — в статье что такое return.
Честный совет: если видишь forEach, почти всегда его можно заменить на for...of — и получить break и await бесплатно.
Как починить ловушку с await
Вернёмся к письмам. Если нужно по очереди — нужен for...of:
for (const user of users) {
await send(user) // ждём, потом следующий
}
Если можно все сразу, но надо дождаться, когда закончатся все, — map вместе с Promise.all:
await Promise.all(users.map(user => send(user)))
Второй вариант быстрее, первый — бережнее к чужим серверам. Про асинхронность целиком — в статье что такое async.
Кому что подойдёт
Вердикт без дипломатии:
- Бери
map, когда из одного списка делаешь другой: цены с налогом, имена из пользователей, карточки в React. Это 80% случаев в интерфейсе. - Бери
for...of, когда делаешь действия: отправляешь, сохраняешь, ищешь первый подходящий и останавливаешься, ждёшь ответа сервера на каждом шаге. forEachне бери. Он ничего не умеет лучше двух соседей.
В промпт для ИИ достаточно одной фразы: «запросы к серверу — по очереди через for...of, без map с async». Модель перестанет устраивать тебе случайный DDoS.
Что быстрее: for или map?
Для списков в сотни и тысячи элементов разница незаметна. Выбирай по смыслу, а не по скорости. Скорость начинает волновать на миллионах элементов — и там уже обычно другие проблемы.
Можно ли в map изменить исходный массив?
Технически можно, но не нужно. Смысл map в том, что старый список остаётся нетронутым, а ты получаешь новый. Если хочешь менять на месте — это работа для цикла.
Что использовать, чтобы отфильтровать список?
Ни то, ни другое — для этого есть filter. Он возвращает новый массив только с подходящими элементами: users.filter(u => u.isActive).
Короткие уроки-истории, симулятор агента и ежедневная практика — в нашем мобильном приложении. Бесплатно.





