Как положить результат промиса в массив по индексу в JavaScript?
Что проверяют
Знаете ли вы, что Promise.all сам сохраняет порядок, и понимаете ли, чем порядок завершения отличается от порядка запуска. Следом почти всегда спрашивают, что будет, если один из промисов упадёт, и как ограничить число одновременных запросов.
Как отвечать
Самый короткий ответ: ручная раскладка по индексам обычно не нужна.
const results = await Promise.all(ids.map((id) => fetchItem(id)));results[i] всегда соответствует ids[i], в каком бы порядке ни пришли ответы. Дальше разберите варианты:
- запись по индексу вручную:
promise.then((v) => { arr[i] = v; })нужна, когда результаты складываются в уже существующую структуру или показываются по мере прихода. Индекс берётся из замыкания, поэтому цикл пишут наletили черезmap((item, i) => ...). Сvarвсе колбэки запишут значение в один и тот же индекс, равный длине массива; Promise.allSettled: если частичный результат лучше никакого. Каждый элемент приходит объектом{ status, value }или{ status, reason }, и по индексу видно, что именно не загрузилось;- ограничение параллельности: сто запросов разом упрутся в лимиты сервера. Решение: пул воркеров, которые берут следующий индекс из общего счётчика, или готовая библиотека вроде
p-limit. Порядок держится за счёт того же индекса; - последовательно:
for...ofсawaitвнутри, когда каждый шаг зависит от предыдущего. Это медленнее ровно во столько раз, сколько элементов.
Где спотыкаются
Пишут results.push(v) внутри then. Массив заполняется в порядке ответов сети, а не в порядке запросов, и на локальной машине баг не виден, потому что всё приходит почти одновременно.
Используют forEach с async-колбэком и ждут, что код после него увидит заполненный массив. forEach возвращаемые промисы игнорирует, ждать нечего.
Забывают про поведение Promise.all при ошибке: он отклоняется на первой же ошибке, остальные запросы продолжают выполняться, но их результаты вы уже не получите. Если это неприемлемо, нужен allSettled или catch на каждом промисе.