When working with Ajax in React or jQuery, developers often encounter scenarios where they need to abort an ongoing request — for example, when a component unmounts, a user quickly changes input, or multiple requests are made in succession. So how can developers efficiently cancel these Ajax calls?
To handle these cases efficiently, JavaScript provides a modern solution: the AbortController.
What is AbortController?
The AbortController is a JavaScript interface that allows you to abort or cancel one or more DOM requests (like fetch/Ajax requests) as and when desired.It consists of two main parts:
- AbortController - The controller object that triggers cancellation.
- AbortSignal - The signal object that notifies when cancellation occurs.
Key Properties & Methods:
- controller.abort() → Cancels the operation.
- signal.aborted → true if aborted, false otherwise.
- signal.reason → Contains the abort reason (default: DOMException with "AbortError").
Browser Support:
AbortController is well-supported across all major modern browsers:
- Chrome 66+
- Firefox 57+
- Safari 12.1+
- Edge 16+
Below are the few examples and explanation of how we use AbortController in jQuery
const controller = new AbortController();
$.ajax({
url: 'https://jsonplaceholder.typicode.com/posts/1',
method: 'GET',
signal: controller.signal, // ✅ pass the signal
success: function (data) {
console.log('Data:', data);
},
error: function (jqXHR, textStatus, errorThrown) {
if (textStatus === 'abort') {
console.log('Request aborted');
} else {
console.error('Error:', errorThrown);
}
}
});
// Abort the request (e.g. on some condition or timeout)
controller.abort();
The Code above shows how can we use AbortController in jQuery. The key Line is 'signal: controller.signal'.
The signal property of an AbortController is an AbortSignal object that serves as the communication channel between the controller and any abortable operations..
Understanding the AbortSignal
Key Properties and Methods
Properties- aborted (readonly boolean)
- true if the controller has been aborted
- false otherwise
- reason (readonly)
- When aborted, contains the abort reason (defaults to a DOMException with name "AbortError")
- Can be any value passed to controller.abort(reason)
- abort event
- Fired when the controller's abort() method is called
- Can be listened to with addEventListener
Usage Examples
Basic Check
const controller = new AbortController();
console.log(controller.signal.aborted); // false
controller.abort();
console.log(controller.signal.aborted); // true
Custom Abort Reason
const controller = new AbortController();
controller.abort('User cancelled operation');
console.log(controller.signal.reason); // 'User cancelled operation'
Event Listening
const controller = new AbortController();
controller.signal.addEventListener('abort', () => {
console.log('Abort triggered!');
console.log('Reason:', controller.signal.reason);
});
controller.abort('Manual cancellation');
Some More examples of AbortController using jQuery
jQuery $.ajax() + AbortController + Timeout Example
const controller = new AbortController();
// Set a timeout to abort the request after 3 seconds
const timeoutId = setTimeout(() => {
controller.abort();
console.log('Request aborted after timeout ⏱');
}, 3000);
$.ajax({ url: 'https://jsonplaceholder.typicode.com/posts/1',
method: 'GET',
signal: controller.signal, // pass the signal to allow abort
success: function (data) {
clearTimeout(timeoutId); // cancel timeout if successful
console.log('✅ Data:', data);
},
error: function (jqXHR, textStatus, errorThrown) {
if (textStatus === 'abort') {
console.log('❌ Request was aborted');
} else {
console.error('⚠️ Other error:', errorThrown);
}
}
});
Explain
- AbortController gives us a .signal we pass to $.ajax.
- We use setTimeout() to trigger .abort() after 3 seconds.
- If the request finishes before that, we cancel the timeout using clearTimeout().
Question: Can we use single AbortController with Multiple AJAX Request in Parallel
No it's not recommended to use a single AbortController for multiple parallel AJAX requests. The reason is:
- Calling controller.abort() will cancel all requests that share the same signal.
- If multiple requests are using the same controller, aborting one will unintentionally abort the others as well.
What to do instead?
For parallel requests, it's best to create a separate AbortController for each:
Here is an Example:const controller1 = new AbortController();
const controller2 = new AbortController();
$.ajax({
url: 'https://api.example.com/data1',
signal: controller1.signal,
success: data => console.log('Data 1:', data),
error: (jqXHR, textStatus) => {
if (textStatus === 'abort') console.log('Request 1 aborted');
}
});
$.ajax({
url: 'https://api.example.com/data2',
signal: controller2.signal,
success: data => console.log('Data 2:', data),
error: (jqXHR, textStatus) => {
if (textStatus === 'abort') console.log('Request 2 aborted');
}
});
// Later, you can abort them individually
controller1.abort(); // aborts only the first
controller2.abort(); // aborts only the second
What if we want to abort all at once?
If you want to group several requests and abort them together, using a single controller is the way to go:
const controller = new AbortController();
const urls = ['/api/1', '/api/2', '/api/3'];
urls.forEach(url => {
$.ajax({
url,
signal: controller.signal,
success: data => console.log(`✅ Data from ${url}:`, data),
error: (jqXHR, textStatus) => {
if (textStatus === 'abort') console.log(`❌ Aborted: ${url}`);
}
});
});
// Abort all of them together
controller.abort();
React AbortController Example
Now, let’s dive into a complete React example demonstrating how to elegantly incorporate AbortController with fetch in a component. We’ll also include a crucial cleanup step to prevent memory leaks and ensure smooth performance:
import { useState, useEffect } from 'react';
function DataFetcher() {
const [data, setData] = useState(null);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
useEffect(() => {
const controller = new AbortController();
const signal = controller.signal;
const fetchData = async () => {
setLoading(true);
try {
const response = await fetch('https://jsonplaceholder.typicode.com/posts/1', {
signal
});
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
const json = await response.json();
setData(json);
setError(null);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
}
} finally {
setLoading(false);
}
};
fetchData();
// Cleanup function to abort the request when component unmounts
return () => {
controller.abort();
};
}, []); // Empty dependency array means this runs once on mount
const handleManualAbort = () => {
// In a real app, you would store the controller in a ref
// to access it here
console.log('Manual abort would go here');
};
return (
<div>
<h2>Data Fetcher with AbortController</h2>
{loading && <p>Loading...</p>}
{error && <p style={{ color: 'red' }}>Error: {error}</p>}
{data && (
<div>
<h3>{data.title}</h3>
<p>{data.body}</p>
</div>
)}
<button onClick={handleManualAbort}>Cancel Request</button>
</div>
);
}
export default DataFetcher;
This code demonstrates how to use AbortController in a React component to fetch data while managing loading, error, and abort states. It includes an automatic cleanup to abort the request when the component unmounts, and a manual abort function placeholder.
Key Points in This Example:
- Cleanup on Unmount:
- The useEffect cleanup function aborts pending requests when the component unmounts
- Error Handling:
- Specifically checks for AbortError to avoid setting error state when request was intentionally cancelled
- State Management:
- Tracks loading, error, and data states for proper UI feedback
- Manual Abort:
- The button shows where you would implement manual cancellation (would need to store controller in a ref)
Grouping and Cancelling Multiple Requests with AbortController in React
Here's a complete React example that demonstrates how to group multiple fetch requests and cancel them all together using a single AbortController:
import { useState, useEffect, useRef } from 'react';
function MultiRequestFetcher() {
const [data, setData] = useState([]);
const [loading, setLoading] = useState(false);
const [error, setError] = useState(null);
const controllerRef = useRef(null);
// Array of endpoints to fetch
const endpoints = [
'https://jsonplaceholder.typicode.com/posts/1',
'https://jsonplaceholder.typicode.com/posts/2',
'https://jsonplaceholder.typicode.com/posts/3'
];
const fetchAllData = async () => {
// Create a new AbortController for this batch of requests
controllerRef.current = new AbortController();
const { signal } = controllerRef.current;
setLoading(true);
setError(null);
try {
// Create an array of fetch promises
const fetchPromises = endpoints.map(endpoint =>
fetch(endpoint, { signal })
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
);
// Wait for all requests to complete
const results = await Promise.all(fetchPromises);
setData(results);
} catch (err) {
if (err.name !== 'AbortError') {
setError(err.message);
}
} finally {
setLoading(false);
}
};
const abortAllRequests = () => {
if (controllerRef.current) {
controllerRef.current.abort();
console.log('All requests aborted');
}
};
// Cleanup on unmount
useEffect(() => {
return () => {
if (controllerRef.current) {
controllerRef.current.abort();
}
};
}, []);
return (
<div>
<h2>Multiple Requests with Shared AbortController</h2>
<div style={{ marginBottom: '20px' }}>
<button onClick={fetchAllData} disabled={loading}>
Fetch All Data
</button>
<button
onClick={abortAllRequests}
disabled={!loading}
style={{ marginLeft: '10px', backgroundColor: '#ff4444' }}
>
Cancel All Requests
</button>
</div>
{loading && <p>Loading data from {endpoints.length} endpoints...</p>}
{error && (
<p style={{ color: 'red' }}>Error: {error}</p>
)}
{data.length > 0 && (
<div>
<h3>Results:</h3>
<ul style={{ listStyle: 'none', padding: 0 }}>
{data.map((item, index) => (
<li key={index} style={{ marginBottom: '20px', padding: '10px', border: '1px solid #ddd' }}>
<h4>{item.title}</h4>
<p>{item.body}</p>
</li>
))}
</ul>
</div>
)}
</div>
);
This code demonstrates how to make multiple parallel API requests in React using a shared AbortController to cancel all requests at once. It allows fetching data from multiple endpoints, and provides a button to abort all ongoing requests if needed.
How it works?
- When "Fetch All Data" is clicked:
- A new AbortController is created
- Multiple fetch requests are initiated with the same signal
- All requests are tracked with Promise.all
- When "Cancel All Requests" is clicked:
- The abort() method is called on the shared controller
- All fetch requests that used that signal are immediately aborted
- The abort is caught in the error handler (but not shown as an error)
- On component unmount:
- The effect cleanup function ensures no requests are left hanging
- Any pending requests are automatically canceled
- Prevents memory leaks and state updates on unmounted components
Conclusion
Aborting Ajax calls is an essential part of building responsive, efficient web apps. With AbortController, it's now easier than ever to:
- Prevent memory leaks
- Cancel irrelevant or outdated requests
- Keep your UI clean and predictable Whether you're using React with fetch or jQuery 3.5+, integrating AbortController into your workflow is a smart move for modern frontend development.
This blog is brought to you by AARK TECH HUB- your trusted partner for innovative tech solutions and expert guidance in AbortController, React, jQuery, and web development. Stay tuned for more insights and tutorials to elevate your development journey! At AARK TECH HUB, we are committed to empowering businesses with cutting-edge technology, delivering tailored solutions, and helping developers unlock their full potential. Stay tuned for more insights and tutorials to elevate your development journey with our industry-leading expertise!