贵港市聚强科技

AJAX请求次数过多的四种解决方案

2026-04-02 17:42:01 浏览次数:0
详细信息

AJAX请求过多会导致服务器压力大、网络负载高、用户体验差。以下是四种常见解决方案:

1. 请求合并与批处理

将多个小请求合并为单个大请求,减少请求次数:

// 示例:批量获取用户信息
async function batchGetUserInfo(userIds) {
  // 将多个ID合并为一次请求
  const response = await fetch('/api/users/batch', {
    method: 'POST',
    headers: { 'Content-Type': 'application/json' },
    body: JSON.stringify({ ids: userIds })
  });
  return response.json();
}

// 或者使用请求队列
class RequestBatcher {
  constructor(batchSize = 10, delay = 100) {
    this.queue = [];
    this.batchSize = batchSize;
    this.delay = delay;
    this.timeout = null;
  }

  addRequest(request) {
    this.queue.push(request);
    if (this.queue.length >= this.batchSize) {
      this.executeBatch();
    } else if (!this.timeout) {
      this.timeout = setTimeout(() => this.executeBatch(), this.delay);
    }
  }

  async executeBatch() {
    if (this.timeout) {
      clearTimeout(this.timeout);
      this.timeout = null;
    }

    if (this.queue.length === 0) return;

    const batch = this.queue.splice(0, this.batchSize);
    // 执行批量请求
    await this.sendBatchRequest(batch);
  }
}

2. 请求防抖与节流

控制请求频率,避免过于频繁的请求:

// 防抖:等待用户停止操作后再请求
function debounce(func, wait) {
  let timeout;
  return function(...args) {
    clearTimeout(timeout);
    timeout = setTimeout(() => func.apply(this, args), wait);
  };
}

// 节流:固定时间间隔内只执行一次
function throttle(func, limit) {
  let inThrottle;
  return function(...args) {
    if (!inThrottle) {
      func.apply(this, args);
      inThrottle = true;
      setTimeout(() => inThrottle = false, limit);
    }
  };
}

// 使用示例
const searchInput = document.getElementById('search');
const searchHandler = debounce(async (event) => {
  const query = event.target.value;
  if (query.length > 2) {
    const results = await fetch(`/api/search?q=${query}`);
    // 更新UI
  }
}, 300);

searchInput.addEventListener('input', searchHandler);

3. 缓存机制

实现客户端缓存,减少重复请求:

class RequestCache {
  constructor(defaultTTL = 300000) { // 默认5分钟
    this.cache = new Map();
    this.defaultTTL = defaultTTL;
  }

  async get(url, options = {}) {
    const cacheKey = this.createKey(url, options);
    const cached = this.cache.get(cacheKey);

    if (cached && Date.now() < cached.expiry) {
      return cached.data;
    }

    // 缓存未命中或已过期
    const response = await fetch(url, options);
    const data = await response.json();

    this.set(cacheKey, data, options.ttl || this.defaultTTL);
    return data;
  }

  set(key, data, ttl) {
    this.cache.set(key, {
      data,
      expiry: Date.now() + ttl
    });
  }

  createKey(url, options) {
    return JSON.stringify({ url, options });
  }

  // 可选:定时清理过期缓存
  startCleanup(interval = 60000) {
    setInterval(() => {
      const now = Date.now();
      for (const [key, value] of this.cache.entries()) {
        if (now >= value.expiry) {
          this.cache.delete(key);
        }
      }
    }, interval);
  }
}

// 使用缓存
const cache = new RequestCache();

async function getProductInfo(productId) {
  return cache.get(`/api/products/${productId}`);
}

4. WebSocket或Server-Sent Events

对于实时性要求高的场景,使用长连接替代频繁的AJAX轮询:

// WebSocket示例
class RealTimeService {
  constructor(url) {
    this.ws = new WebSocket(url);
    this.subscribers = new Map();
    this.setupWebSocket();
  }

  setupWebSocket() {
    this.ws.onmessage = (event) => {
      const data = JSON.parse(event.data);
      const { type, payload } = data;

      // 通知对应类型的订阅者
      if (this.subscribers.has(type)) {
        this.subscribers.get(type).forEach(callback => callback(payload));
      }
    };

    this.ws.onopen = () => {
      console.log('WebSocket连接已建立');
    };
  }

  subscribe(type, callback) {
    if (!this.subscribers.has(type)) {
      this.subscribers.set(type, []);
    }
    this.subscribers.get(type).push(callback);
  }

  send(type, data) {
    this.ws.send(JSON.stringify({ type, data }));
  }
}

// Server-Sent Events (SSE) 示例
class SSEClient {
  constructor(url) {
    this.eventSource = new EventSource(url);
    this.callbacks = new Map();

    this.eventSource.onmessage = (event) => {
      const data = JSON.parse(event.data);
      this.handleEvent(data);
    };
  }

  on(eventType, callback) {
    this.eventSource.addEventListener(eventType, (event) => {
      callback(JSON.parse(event.data));
    });
  }
}

最佳实践组合方案

实际项目中通常组合使用多种方案:

class OptimizedAPIClient {
  constructor() {
    this.cache = new RequestCache();
    this.pendingRequests = new Map();
  }

  async request(url, options = {}) {
    const {
      useCache = true,
      debounceTime = 0,
      batchKey = null
    } = options;

    // 1. 检查缓存
    if (useCache) {
      const cached = this.cache.get(url);
      if (cached) return cached;
    }

    // 2. 防抖处理
    if (debounceTime > 0) {
      return this.debouncedRequest(url, options, debounceTime);
    }

    // 3. 批处理
    if (batchKey) {
      return this.batchRequest(url, options, batchKey);
    }

    // 普通请求
    return this.fetchWithTimeout(url, options);
  }

  async batchRequest(url, options, batchKey) {
    // 实现批处理逻辑
  }

  debouncedRequest(url, options, wait) {
    // 实现防抖逻辑
  }

  async fetchWithTimeout(url, options) {
    const controller = new AbortController();
    const timeoutId = setTimeout(() => controller.abort(), 10000);

    try {
      const response = await fetch(url, {
        ...options,
        signal: controller.signal
      });
      clearTimeout(timeoutId);
      return response.json();
    } catch (error) {
      clearTimeout(timeoutId);
      throw error;
    }
  }
}

选择建议

场景 推荐方案 理由
搜索框实时搜索 防抖 + 缓存 减少无效请求,快速响应
数据仪表盘 批处理 + WebSocket 减少连接数,实时更新
电商商品列表 缓存 + 节流 快速加载,避免重复请求
聊天应用 WebSocket 实时双向通信

根据具体业务场景,选择合适的组合方案,可以有效优化AJAX请求过多的问题。

相关推荐