Chapter 7: Request Cancellation and Concurrency Control

Haiyue
5min

Chapter 7: Request Cancellation and Concurrency Control

Learning Objectives

  1. Master the use of CancelToken
  2. Learn to cancel requests with AbortController
  3. Understand handling concurrent requests
  4. Implement request deduplication mechanism
  5. Master request queue management

7.1 Canceling Requests

Using CancelToken

// Create cancel token
const source = axios.CancelToken.source();

// Send cancelable 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 request
source.cancel('User canceled operation');
// 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 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 same request exists, return existing Promise
    if (this.pendingRequests.has(key)) {
      console.log('Duplicate request found, returning existing Promise');
      return this.pendingRequests.get(key);
    }

    // Create 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 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;
  }
}

// Handle partial failure scenarios
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

  • Request cancellation mechanism prevents wasteful network overhead
  • Request deduplication avoids wasting resources on duplicate requests
  • Concurrency control protects server and client performance
  • Batch processing improves data retrieval efficiency

Key Points

  • Use AbortController instead of deprecated CancelToken
  • Reasonable deduplication strategy improves user experience
  • Concurrency control avoids server overload
  • Batch requests need to consider partial failure scenarios