Как использовать async/await с Array.map в TypeScript?
Проблема: map не ожидает async функции
Вы столкнулись с классической проблемой: метод Array.map синхронный и не умеет работать с промисами. Когда вы передаете асинхронную функцию (async), она немедленно возвращает промис (Promisemap возвращает массив промисов Promise, а не Post[].
Решение: комбинация map + Promise.all
Правильный подход — использовать Promise.all, который принимает массив промисов и возвращает единый промис, который разрешается, когда все промисы в массиве завершены. Вот исправленный код:
async function fetchAllPosts(ids: number[]): Promise<Post[]> {
// 1. Создаем массив промисов с помощью map
const promises = ids.map(async (id) => {
const post = await fetchPost(id);
return post;
});
// 2. Дожидаемся всех промисов сразу
const posts = await Promise.all(promises);
return posts;
}
// Или более краткий вариант:
async function fetchAllPostsShort(ids: number[]): Promise<Post[]> {
return await Promise.all(ids.map(id => fetchPost(id)));
}
// Использование:
const result = await fetchAllPosts([1, 2, 3]);
console.log(result); // ✅ [Post, Post, Post]
Как это работает
mapсоздает массив промисов (каждый вызовfetchPostвозвращает промис)Promise.allпринимает этот массив и возвращает новый промис- Мы ждем его с помощью
await, получая массив результатов - Все запросы выполняются параллельно, что быстрее последовательного выполнения
Обработка ошибок
Если один из запросов упадет, Promise.all сразу завершится с ошибкой (режим "fail-fast"). Чтобы обрабатывать ошибки для каждого запроса отдельно, используйте Promise.allSettled (ES2020) или оборачивайте каждый запрос в try/catch:
// Вариант 1: Promise.allSettled (получаем статус каждого запроса)
async function fetchAllPostsWithStatus(ids: number[]): Promise<(Post | null)[]> {
const results = await Promise.allSettled(
ids.map(id => fetchPost(id))
);
return results.map(result => {
if (result.status === 'fulfilled') {
return result.value; // успешный результат
} else {
console.error('Ошибка загрузки:', result.reason);
return null; // или выбрасываем ошибку
}
});
}
// Вариант 2: try/catch внутри map
async function fetchAllPostsSafe(ids: number[]): Promise<Post[]> {
const results = await Promise.all(
ids.map(async (id) => {
try {
return await fetchPost(id);
} catch (error) {
console.error(`Ошибка загрузки поста ${id}:`, error);
// Можно вернуть значение по умолчанию или null
return { id, title: 'Ошибка загрузки', body: '' } as Post;
}
})
);
return results;
}
Важные замечания
- Типизация: TypeScript автоматически выводит тип
Promise<Post[]>изPromise.all - Производительность: Все запросы выполняются параллельно. Если нужно ограничить количество одновременных запросов, используйте библиотеки вроде
p-limit - Альтернатива: Для последовательного выполнения (один запрос за другим) используйте цикл
for...ofс await внутри
Теперь ваша функция будет работать корректно и возвращать массив постов, а не промисов!
<<Дополнительный подход: последовательное выполнение с for...of
Иногда вам нужно выполнять асинхронные операции последовательно, а не параллельно. Например, если вы делаете запросы к API с ограничением по частоте (rate limiting) или каждый следующий запрос зависит от предыдущего. В этом случае Promise.all не подходит, и лучше использовать цикл for...of:
async function fetchAllPostsSequential(ids: number[]): Promise<Post[]> {
const posts: Post[] = [];
for (const id of ids) {
const post = await fetchPost(id);
posts.push(post);
}
return posts;
}
// Использование:
const result = await fetchAllPostsSequential([1, 2, 3]);
// Запросы выполняются по очереди: сначала пост 1, потом 2, потом 3
Когда использовать
- for...of + await: когда важен порядок выполнения или есть ограничения на количество запросов
- map + Promise.all: когда запросы независимы и нужно максимальное быстродействие
Также обратите внимание на Array.reduce для последовательной цепочки промисов, но это менее читаемый вариант. Для большинства случаев с независимыми запросами используйте первый ответ с Promise.all — это и быстрее, и проще для понимания.
Ответов пока нет.
Войдите чтобы оставить ответ