Chapter 7: Request Cancellation and Concurrency Control
9/1/25About 1 min
Chapter 7: Request Cancellation and Concurrency Control
Learning Objectives
- Master the use of CancelToken
- Learn to cancel requests with AbortController
- Understand how to handle concurrent requests
- Implement a request deduplication mechanism
- Master request queue management
7.1 Canceling Requests
Using CancelToken
// Create a cancel token
const source = axios.CancelToken.source();
// Send a cancellable request
axios.get('/api/data', {
cancelToken: source.token
}).catch(function (thrown) {
if (axios.isCancel(thrown)) {
console.log('Request canceled:', thrown.message);
} else {
console.error('Request error:', thrown);
}
});
// Cancel the request
source.cancel('User canceled operation');
Using AbortController (Recommended)
// Modern browsers recommend using AbortController
const controller = new AbortController();
axios.get('/api/data', {
signal: controller.signal
}).catch(function (error) {
if (error.name === 'AbortError') {
console.log('Request aborted');
}
});
// Cancel the request
controller.abort();
7.2 Request Deduplication
Preventing Duplicate Requests
class RequestDeduplicator {
constructor() {
this.pendingRequests = new Map();
}
generateKey(config) {
return `${config.method}-${config.url}-${JSON.stringify(config.params)}`;
}
async request(config) {
const key = this.generateKey(config);
// If an identical request exists, return the existing Promise
if (this.pendingRequests.has(key)) {
console.log('Duplicate request found, returning existing Promise');
return this.pendingRequests.get(key);
}
// Create a new request
const promise = axios(config)
.finally(() => {
this.pendingRequests.delete(key);
});
this.pendingRequests.set(key, promise);
return promise;
}
}
const deduplicator = new RequestDeduplicator();
7.3 Concurrency Control
Limiting the Number of Concurrent Requests
class ConcurrencyController {
constructor(maxConcurrent = 5) {
this.maxConcurrent = maxConcurrent;
this.running = 0;
this.queue = [];
}
async request(config) {
return new Promise((resolve, reject) => {
this.queue.push({ config, resolve, reject });
this.processQueue();
});
}
async processQueue() {
if (this.running >= this.maxConcurrent || this.queue.length === 0) {
return;
}
this.running++;
const { config, resolve, reject } = this.queue.shift();
try {
const response = await axios(config);
resolve(response);
} catch (error) {
reject(error);
} finally {
this.running--;
this.processQueue();
}
}
}
const concurrencyController = new ConcurrencyController(3);
7.4 Batch Request Handling
Promise.all and Promise.allSettled
// Execute all requests in parallel
async function batchRequests() {
try {
const [users, posts, comments] = await Promise.all([
axios.get('/api/users'),
axios.get('/api/posts'),
axios.get('/api/comments')
]);
return { users: users.data, posts: posts.data, comments: comments.data };
} catch (error) {
console.error('Batch request failed:', error);
throw error;
}
}
// Handling partially failed cases
async function robustBatchRequests() {
const results = await Promise.allSettled([
axios.get('/api/users'),
axios.get('/api/posts'),
axios.get('/api/comments')
]);
const successful = results.filter(result => result.status === 'fulfilled')
.map(result => result.value.data);
const failed = results.filter(result => result.status === 'rejected')
.map(result => result.reason);
return { successful, failed };
}
Chapter Summary
- The request cancellation mechanism prevents unnecessary network overhead
- Request deduplication avoids wasting resources on duplicate requests
- Concurrency control protects server and client performance
- Batch processing improves data retrieval efficiency
Key Takeaways
- Use AbortController instead of the deprecated CancelToken
- A reasonable deduplication strategy improves user experience
- Concurrency control prevents server overload
- Batch requests need to consider partially failed cases