插件窝 干货文章 多个浏览器标签页同时请求同一消息模块,如何避免重复请求?

多个浏览器标签页同时请求同一消息模块,如何避免重复请求?

请求 localStorage messageRequestIn Progress 535    来源:    2025-03-25

避免浏览器标签页重复请求同一消息模块的解决方案

当多个浏览器标签页同时请求同一消息模块时,可能会导致服务器资源浪费和数据不一致问题。以下是几种有效的解决方案:

1. 使用共享存储方案

1.1 localStorage + StorageEvent

// 主标签页
if (!localStorage.getItem('messageRequestInProgress')) {
  localStorage.setItem('messageRequestInProgress', 'true');
  fetchMessages().then(data => {
    // 处理数据
    localStorage.removeItem('messageRequestInProgress');
  });
}

// 其他标签页监听
window.addEventListener('storage', (event) => {
  if (event.key === 'messageRequestInProgress') {
    if (!event.newValue) {
      // 请求已完成,可以安全请求
    }
  }
});

1.2 SharedWorker

创建一个共享Worker来管理请求状态。

2. 服务端控制方案

2.1 请求去重令牌

// 生成唯一请求ID
const requestId = Date.now() + '-' + Math.random().toString(36).substr(2, 9);

fetch('/api/messages', {
  headers: {
    'X-Request-ID': requestId
  }
});

服务端可以记录最近处理的请求ID,避免重复处理。

2.2 短时间缓存

服务端对相同请求在短时间内返回缓存结果。

3. 浏览器API方案

3.1 BroadcastChannel API

const channel = new BroadcastChannel('message_requests');

// 发起请求前广播
channel.postMessage({type: 'request_start'});

// 监听其他标签页的请求
channel.addEventListener('message', (event) => {
  if (event.data.type === 'request_start') {
    // 取消或延迟自己的请求
  }
});

4. 综合最佳实践

  1. 客户端优先检查:使用localStorage检查是否有进行中的请求
  2. 服务端验证:在请求中包含客户端生成的唯一ID
  3. 结果共享:通过BroadcastChannel或storage事件共享获取的数据
  4. 指数退避:如果检测到冲突,采用随机延迟重试机制
async function fetchMessagesWithDedupe() {
  const requestId = generateUniqueId();

  // 检查是否有其他标签页正在请求
  if (localStorage.getItem('messageRequestInProgress')) {
    await new Promise(resolve => 
      setTimeout(resolve, 100 + Math.random() * 500));
    return fetchMessagesWithDedupe();
  }

  try {
    localStorage.setItem('messageRequestInProgress', requestId);
    const response = await fetch('/api/messages', {
      headers: {'X-Request-ID': requestId}
    });

    // 广播结果给其他标签页
    new BroadcastChannel('message_requests').postMessage({
      type: 'messages_data',
      data: await response.json()
    });

    return response;
  } finally {
    localStorage.removeItem('messageRequestInProgress');
  }
}

选择哪种方案取决于您的具体应用场景、浏览器支持要求和系统架构。对于现代浏览器,推荐结合BroadcastChannel和localStorage的方案。