当多个浏览器标签页同时请求同一消息模块时,可能会导致服务器资源浪费和数据不一致问题。以下是几种有效的解决方案:
// 主标签页
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) {
// 请求已完成,可以安全请求
}
}
});
创建一个共享Worker来管理请求状态。
// 生成唯一请求ID
const requestId = Date.now() + '-' + Math.random().toString(36).substr(2, 9);
fetch('/api/messages', {
headers: {
'X-Request-ID': requestId
}
});
服务端可以记录最近处理的请求ID,避免重复处理。
服务端对相同请求在短时间内返回缓存结果。
const channel = new BroadcastChannel('message_requests');
// 发起请求前广播
channel.postMessage({type: 'request_start'});
// 监听其他标签页的请求
channel.addEventListener('message', (event) => {
if (event.data.type === 'request_start') {
// 取消或延迟自己的请求
}
});
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的方案。