Handling Multiple API Calls with Promise.all

Loading

When building React applications that rely on multiple external data sources, it’s common to make multiple API calls at once. Instead of waiting for each API call to complete one by one, you can execute them in parallel. Using JavaScript’s Promise.all, you can efficiently manage multiple API calls simultaneously.

Promise.all is a powerful method that allows you to run multiple promises in parallel and wait for all of them to resolve before proceeding with any further operations.


1. What is Promise.all?

Promise.all takes an array of promises as input and returns a single promise. This returned promise resolves when all the input promises have resolved successfully. If any of the input promises rejects, the returned promise will immediately reject with that error.

Syntax:

Promise.all([promise1, promise2, promise3])
  .then(results => {
    // all promises resolved successfully
  })
  .catch(error => {
    // one of the promises rejected
  });

2. Using Promise.all in React for Multiple API Calls

You can use Promise.all in React to handle multiple API calls simultaneously. Here’s an example of how you can do this in a React functional component.

Example: Making Multiple API Calls with Promise.all

import React, { useEffect, useState } from 'react';

// Function to fetch data
const fetchData = (url) => fetch(url).then((res) => res.json());

const App = () => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    // Define multiple API endpoints
    const endpoints = [
      'https://api.example.com/data1',
      'https://api.example.com/data2',
      'https://api.example.com/data3',
    ];

    // Use Promise.all to make parallel API calls
    Promise.all(endpoints.map((url) => fetchData(url)))
      .then((results) => {
        // Handle successful API calls
        setData(results);
        setLoading(false);
      })
      .catch((err) => {
        // Handle any error that occurs during any API call
        setError(err);
        setLoading(false);
      });
  }, []);

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

  return (
    <div>
      <h1>API Data:</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

export default App;

Explanation:

  • fetchData function: This function fetches data from a given URL and returns the response in JSON format.
  • useEffect hook: We use useEffect to initiate the API calls when the component mounts.
  • Promise.all: The Promise.all method is used to initiate multiple API calls at the same time. The results are returned as an array, which is stored in the data state.
  • Error Handling: If any of the promises reject, the catch block handles the error, and an error message is displayed.

3. Error Handling with Promise.all

One of the key things to remember when using Promise.all is that if any of the promises in the array fail (reject), the entire Promise.all will immediately reject, and no results will be returned.

Handling Errors in Parallel Calls

import React, { useEffect, useState } from 'react';

// Function to fetch data
const fetchData = (url) => fetch(url).then((res) => res.json());

const App = () => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const endpoints = [
      'https://api.example.com/data1',
      'https://api.example.com/data2',
      'https://api.example.com/data3',
    ];

    Promise.all(endpoints.map((url) => fetchData(url)))
      .then((results) => {
        setData(results);
        setLoading(false);
      })
      .catch((err) => {
        setError("Failed to fetch data from one or more APIs.");
        setLoading(false);
      });
  }, []);

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

  return (
    <div>
      <h1>API Data:</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

export default App;

Explanation:

  • If one of the API requests fails, the catch block will handle the error, and an error message will be displayed to the user.
  • You could improve this by handling specific errors from each API separately or retrying failed requests.

4. Handling Multiple API Calls with Promise.all with Different Response Formats

If the different API calls return data in different formats or structures, you can map over the results to shape the data as needed.

Example: Custom Mapping of Results from Multiple API Calls

import React, { useEffect, useState } from 'react';

const fetchData = (url) => fetch(url).then((res) => res.json());

const App = () => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState(null);

  useEffect(() => {
    const endpoints = [
      'https://api.example.com/data1',
      'https://api.example.com/data2',
      'https://api.example.com/data3',
    ];

    Promise.all(endpoints.map((url) => fetchData(url)))
      .then((results) => {
        // Custom mapping based on different response formats
        const [data1, data2, data3] = results;
        setData({
          data1: data1.items,
          data2: data2.details,
          data3: data3.results,
        });
        setLoading(false);
      })
      .catch((err) => {
        setError('Error fetching data.');
        setLoading(false);
      });
  }, []);

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

  return (
    <div>
      <h1>Custom Mapped API Data:</h1>
      <pre>{JSON.stringify(data, null, 2)}</pre>
    </div>
  );
};

export default App;

Explanation:

  • The data from each API call is assumed to have different structures (data1.items, data2.details, data3.results).
  • We map and combine the data from the different API responses to match the needs of our application.

Key Takeaways:

  • Promise.all runs multiple promises concurrently, waiting for all of them to resolve before proceeding.
  • If any promise fails, Promise.all rejects, and no results will be returned, so proper error handling is necessary.
  • Consider using Promise.allSettled if you want to ensure that all promises complete, even if some fail.

In scenarios where more complex handling is needed, consider using additional libraries like React Query or SWR for managing multiple API requests with features like caching, retries, and automatic refetching.


Leave a Reply

Your email address will not be published. Required fields are marked *