如何用单例 Promise 解决重复请求问题

218 阅读2分钟

背景

在实际开发中,前端页面可能会由于用户的快速点击、组件的多次渲染或其他触发机制,导致重复发送相同的请求。这不仅浪费网络资源,还可能导致数据重复加载或状态异常。为了解决这个问题,我们可以使用单例 Promise 模式。

单例 Promise 工具函数

下面是一个简单易用的单例 Promise 工具函数:

export function singletonPromise(fn) {
  const promisesMap = new Map();
  return (...args) => {
    const key = JSON.stringify(args); // 根据参数生成唯一的键
    if (!promisesMap.has(key)) {
      const promise = fn(...args)
        .then((result) => {
          setTimeout(() => { // 1000ms以内的请求,视为重复请求,直接返回上次的结果
            promisesMap.delete(key); // 清理完成的 Promise
          }, 1000);
          return result;
        })
        .catch((error) => {
          promisesMap.delete(key); // 清理失败的 Promise
          throw error;
        });

      promisesMap.set(key, promise);
    }
    return promisesMap.get(key);
  };
}

关键特性

  1. 避免重复请求: 相同参数的请求只会触发一次,后续的调用直接复用已有的 Promise。
  2. 参数唯一键: 使用 JSON.stringify 将参数序列化为唯一标识,适配不同参数的请求场景。
  3. 自动清理: 请求完成(无论成功或失败)后,自动释放对应的键,避免内存泄漏。

使用示例

以下是如何在实际场景中使用该工具函数:

// 创建一个需要单例化的异步函数
const fetchData = singletonPromise((url) =>
  fetch(url).then((res) => res.json())
);

// 示例:发起请求
fetchData('https://api.example.com/data')
  .then((data) => console.log('Data:', data))
  .catch((error) => console.error('Error:', error));

// 即使多次调用相同的 URL,也只会发送一次实际请求
fetchData('https://api.example.com/data');
fetchData('https://api.example.com/data');

// 不同参数的请求会分别发送
fetchData('https://api.example.com/other-data')
  .then((data) => console.log('Other Data:', data));

适用场景

  • 防止重复点击: 按钮或操作多次触发同一个请求时。
  • 组件渲染: 同一数据在多组件中重复请求的问题。
  • 定时轮询: 保证轮询任务中相同参数的请求不会重复发送。

总结

通过单例 Promise 工具函数,可以有效减少重复请求的发生,提高页面的性能和用户体验。简单的实现方式适用于大多数场景,是前端开发中不可或缺的小工具之一。