← Назад к вопросам

Как получить результат трех параллельных асинхронных запросов?

1.7 Middle🔥 111 комментариев
#JavaScript Core

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Как получить результат трех параллельных асинхронных запросов?

Получение результатов нескольких асинхронных операций параллельно — это частая задача. Есть несколько способов, и выбор зависит от твоих требований.

Способ 1: Promise.all() — простой и быстрый

Promise.all() выполняет все промисы параллельно и возвращает результаты в том же порядке.

// Три параллельных запроса
const [users, posts, comments] = await Promise.all([
  fetch('/api/users').then(r => r.json()),
  fetch('/api/posts').then(r => r.json()),
  fetch('/api/comments').then(r => r.json())
]);

console.log('Users:', users);
console.log('Posts:', posts);
console.log('Comments:', comments);

Практический пример:

async function loadDashboard() {
  try {
    const [userData, stats, notifications] = await Promise.all([
      fetch('/api/user').then(r => r.json()),
      fetch('/api/stats').then(r => r.json()),
      fetch('/api/notifications').then(r => r.json())
    ]);

    return {
      user: userData,
      stats,
      notifications
    };
  } catch (error) {
    console.error('Failed to load dashboard:', error);
    // Если любой из запросов упал — весь Promise.all() выбросит ошибку
  }
}

const dashboard = await loadDashboard();

Преимущества:

  • Выполняет запросы параллельно (быстро)
  • Простая синтаксис
  • Результаты в том же порядке

Недостатки:

  • Если один запрос упал — остальные отменяются
  • Может быть медленнее, если не все запросы важны

Способ 2: Promise.allSettled() — надежный

Promise.allSettled() выполняет все промисы и ждет, пока все завершатся, даже если некоторые упадут.

// Все три запроса выполнятся, даже если какие-то упадут
const results = await Promise.allSettled([
  fetch('/api/users').then(r => r.json()),
  fetch('/api/posts').then(r => r.json()),
  fetch('/api/comments').then(r => r.json())
]);

// results = [
//   { status: 'fulfilled', value: [...] },
//   { status: 'rejected', reason: Error },
//   { status: 'fulfilled', value: [...] }
// ]

const [usersResult, postsResult, commentsResult] = results;

if (usersResult.status === 'fulfilled') {
  console.log('Users loaded:', usersResult.value);
} else {
  console.error('Failed to load users:', usersResult.reason);
}

if (postsResult.status === 'fulfilled') {
  console.log('Posts loaded:', postsResult.value);
}

if (commentsResult.status === 'fulfilled') {
  console.log('Comments loaded:', commentsResult.value);
}

Практический пример:

async function loadPageData() {
  const results = await Promise.allSettled([
    fetch('/api/profile').then(r => r.json()),
    fetch('/api/recommendations').then(r => r.json()),
    fetch('/api/analytics').then(r => r.json())
  ]);

  const data = {};

  // Profile — критично, нужно данные
  if (results[0].status === 'fulfilled') {
    data.profile = results[0].value;
  } else {
    throw new Error('Failed to load profile');
  }

  // Recommendations — не критично, может быть пусто
  data.recommendations = results[1].status === 'fulfilled' ? results[1].value : [];

  // Analytics — не критично
  data.analytics = results[2].status === 'fulfilled' ? results[2].value : null;

  return data;
}

Преимущества:

  • Выполняет все запросы, даже если некоторые упадут
  • Можно обработать каждый результат отдельно
  • Подходит для случаев, когда не все данные критичны

Недостатки:

  • Больше кода для обработки результатов
  • Нужно проверять статус каждого результата

Способ 3: Promise.race() — первый результат

Promise.race() возвращает результат первого завершившегося промиса.

// Возвращает результат первого ответившего сервера
const firstResponse = await Promise.race([
  fetch('https://api1.example.com/data').then(r => r.json()),
  fetch('https://api2.example.com/data').then(r => r.json()),
  fetch('https://api3.example.com/data').then(r => r.json())
]);

console.log('Fastest server response:', firstResponse);

Практический пример: timeout

const fetchWithTimeout = (url, timeout = 5000) => {
  const timeoutPromise = new Promise((_, reject) =>
    setTimeout(() => reject(new Error('Request timeout')), timeout)
  );

  return Promise.race([
    fetch(url).then(r => r.json()),
    timeoutPromise
  ]);
};

try {
  const data = await fetchWithTimeout('/api/data', 3000);
  console.log('Data:', data);
} catch (error) {
  console.error(error.message); // "Request timeout" если не ответил
}

Преимущества:

  • Хорош для случаев, когда нужен первый результат
  • Может использоваться для timeout'ов

Недостатки:

  • Другие запросы все равно продолжают выполняться
  • Результаты других запросов игнорируются

Способ 4: Async/Await с независимыми запросами

Если запросы независимы, но нужно их запустить параллельно:

async function loadMultipleResources() {
  // Запускаем все три запроса одновременно
  const usersPromise = fetch('/api/users').then(r => r.json());
  const postsPromise = fetch('/api/posts').then(r => r.json());
  const commentsPromise = fetch('/api/comments').then(r => r.json());

  // Ждем все результаты
  const users = await usersPromise;
  const posts = await postsPromise;
  const comments = await commentsPromise;

  return { users, posts, comments };
}

Важно: нужно запустить все промисы ДО await!

// Плохо: запросы выполняются последовательно (медленно)
async function badExample() {
  const users = await fetch('/api/users').then(r => r.json());
  const posts = await fetch('/api/posts').then(r => r.json());
  const comments = await fetch('/api/comments').then(r => r.json());
  // Время: 3 * delay (последовательно)
}

// Хорошо: запросы выполняются параллельно (быстро)
async function goodExample() {
  const p1 = fetch('/api/users').then(r => r.json());
  const p2 = fetch('/api/posts').then(r => r.json());
  const p3 = fetch('/api/comments').then(r => r.json());
  
  const [users, posts, comments] = await Promise.all([p1, p2, p3]);
  // Время: 1 * delay (параллельно)
}

Способ 5: Комбинированный подход — часть критична, часть нет

Критичные запросы в Promise.all(), второстепенные отдельно:

async function loadPageWithPriority() {
  // Критичные данные
  const [user, posts] = await Promise.all([
    fetch('/api/user').then(r => r.json()),
    fetch('/api/posts').then(r => r.json())
  ]);

  // Второстепенные данные загружаются в фоне
  const recommendationsPromise = fetch('/api/recommendations')
    .then(r => r.json())
    .catch(() => []); // Если упадет, возвращаем пустой массив

  return {
    user,
    posts,
    recommendations: await recommendationsPromise
  };
}

Способ 6: Обработка ошибок в Promise.all()

async function loadDataWithErrorHandling() {
  try {
    const [data1, data2, data3] = await Promise.all([
      fetch('/api/1').then(r => r.json()),
      fetch('/api/2').then(r => r.json()),
      fetch('/api/3').then(r => r.json())
    ]);

    return { data1, data2, data3 };
  } catch (error) {
    console.error('One of the requests failed:', error);
    
    // Можно переспросить или вернуть дефолтные значения
    return {
      data1: null,
      data2: null,
      data3: null
    };
  }
}

Способ 7: Обработка с fallback

const fetchWithFallback = async (url, fallback) => {
  try {
    const response = await fetch(url);
    if (!response.ok) throw new Error(`HTTP ${response.status}`);
    return response.json();
  } catch (error) {
    console.warn(`Failed to fetch ${url}:`, error);
    return fallback;
  }
};

async function loadDashboard() {
  const [users, posts, comments] = await Promise.all([
    fetchWithFallback('/api/users', []),
    fetchWithFallback('/api/posts', []),
    fetchWithFallback('/api/comments', [])
  ]);

  return { users, posts, comments };
}

Практический пример: React компонент

function Dashboard() {
  const [data, setData] = useState({ users: [], posts: [], comments: [] });
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const loadData = async () => {
      try {
        const [users, posts, comments] = await Promise.all([
          fetch('/api/users').then(r => r.json()),
          fetch('/api/posts').then(r => r.json()),
          fetch('/api/comments').then(r => r.json())
        ]);

        setData({ users, posts, comments });
      } catch (err) {
        setError(err.message);
      } finally {
        setLoading(false);
      }
    };

    loadData();
  }, []);

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;

  return (
    <div>
      <h1>Users ({data.users.length})</h1>
      <h1>Posts ({data.posts.length})</h1>
      <h1>Comments ({data.comments.length})</h1>
    </div>
  );
}

Сравнение способов

// 1. Promise.all() — все или ничего
await Promise.all([p1, p2, p3]);
// Быстрый отказ, если хоть один упадет

// 2. Promise.allSettled() — все результаты
await Promise.allSettled([p1, p2, p3]);
// Ждет все, неважно упали или нет

// 3. Promise.race() — первый результат
await Promise.race([p1, p2, p3]);
// Возвращает результат первого завершившегося

// 4. Частичный Promise.all()
await Promise.all([p1, p2]);
const p3Result = await p3; // Отдельно
// Разные стратегии для разных запросов

Заключение

Выбери метод:

  1. Все запросы критичны → Promise.all()
  2. Часть некритична → Promise.allSettled() или смешанный подход
  3. Нужен первый результат → Promise.race()
  4. Нужен контроль над порядком → запусти промисы, потом await

Лучшая практика:

  • Запусти все асинхронные операции ОДНОВРЕМЕННО
  • Используй Promise.all() для критичных данных
  • Обработай ошибки корректно
  • Дай feedback пользователю (loading/error states)
Как получить результат трех параллельных асинхронных запросов? | PrepBro