使用缓存复用同时发起的请求

在现代Web开发中,高效的资源管理和请求处理对于提升用户体验至关重要。特别是在涉及大量API调用或数据获取的应用场景下,减少不必要的网络请求和重复工作可以显著提升应用性能。

复用同时发起的请求

要实现在请求发起时就缓存请求,并让同时发起的相同请求能够复用结果,我们可以采用一种机制来"锁定"正在处理中的请求,并让后续的相同请求等待这个请求完成,然后共享其结果。这通常被称为"请求防抖(debounce)“或"请求节流(throttle)“的一种变体,更具体地,是一种并发控制策略。

并发控制

  • 请求队列:维护一个请求队列或映射表,记录正在处理中的请求及其对应的回调函数。
  • 锁机制:当一个请求开始时,检查是否已有相同请求在处理中。如果有,则将新的回调加入等待列表;如果没有,则启动新的请求,并将此请求标记为“处理中”。

下面是一个简化的示例,展示了如何在 Axios 请求发出前检查是否有相同的请求正在进行,并等待该请求完成,从而复用结果:

// axiosWithConcurrentCache.js
const axios = require('axios');
const LRU = require('lru-cache');

const cache = new LRU({ max: 100, maxAge: 1000 * 60 });
const pendingRequests = new Map(); // 用于存储正在处理中的请求

// 封装的请求函数,实现并发控制和缓存
async function requestWithCache(config) {
  // 生成请求的唯一标识
  const requestKey = JSON.stringify(config);

  // 首先检查缓存
  if (cache.has(requestKey)) {
    console.log(`Using cached response for ${requestKey}`);
    return cache.get(requestKey); // 直接返回缓存结果
  }

  // 检查是否有相同请求正在进行
  if (pendingRequests.has(requestKey)) {
    console.log(`Waiting for ongoing request for ${requestKey}`);
    // 等待正在进行的请求完成
    return new Promise((resolve, reject) => {
      pendingRequests.get(requestKey).push({ resolve, reject });
    });
  }

  // 否则,创建一个新的Promise来代表这个请求,并将其加入到pendingRequests中
  const promiseChain = axios(config)
    .then(response => {
      // 成功后,缓存结果并解析所有等待此请求的Promise
      cache.set(requestKey, response);
      pendingRequests.get(requestKey).forEach(p => p.resolve(response));
      pendingRequests.delete(requestKey);
      return response;
    })
    .catch(error => {
      // 错误处理,同样解析所有等待此请求的Promise
      pendingRequests.get(requestKey).forEach(p => p.reject(error));
      pendingRequests.delete(requestKey);
      throw error;
    });

  // 将新创建的Promise链存储起来,并返回一个新的Promise给当前调用者
  pendingRequests.set(requestKey, []);
  return new Promise((resolve, reject) => {
    pendingRequests.get(requestKey).push({ resolve, reject });
  }).then(() => promiseChain);
}

module.exports = { requestWithCache };

现在,你可以这样使用封装好的请求函数:

// 在其他文件中使用封装的请求函数
const { requestWithCache } = require('./axiosWithConcurrentCache');

requestWithCache({
  method: 'get',
  url: 'https://api.example.com/data'
})
  .then(response => {
    console.log('Data received:', response.data);
  })
  .catch(error => {
    console.error('Error fetching data:', error);
  });

这个模式确保了对于同一个请求,在任何给定的时间内,只有一个实际的网络请求会被发送,而其他相同请求则会等待这个结果并复用它。这样既减少了服务器压力,又提高了客户端的响应速度。

Licensed under CC BY-NC-SA 4.0