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 useuseEffect
to initiate the API calls when the component mounts.Promise.all
: ThePromise.all
method is used to initiate multiple API calls at the same time. The results are returned as an array, which is stored in thedata
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.