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:
fetchDatafunction: This function fetches data from a given URL and returns the response in JSON format.useEffecthook: We useuseEffectto initiate the API calls when the component mounts.Promise.all: ThePromise.allmethod is used to initiate multiple API calls at the same time. The results are returned as an array, which is stored in thedatastate.- Error Handling: If any of the promises reject, the
catchblock 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
catchblock 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.allruns multiple promises concurrently, waiting for all of them to resolve before proceeding.- If any promise fails,
Promise.allrejects, and no results will be returned, so proper error handling is necessary. - Consider using
Promise.allSettledif 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.
