使用 map 对象缓存请求 promise 是实现接口去重的标准做法。通过定义一个全局或模块级的 pendingrequests 映射表,以请求 url 为键,promise 为值。在发起请求前检查该键是否存在,若存在则直接返回已缓存的 promise,否则创建新请求并存入表中,同时在 promise 的 finally 回调中移除该键,确保后续相同请求能重新发起。

如何构建并管理请求缓存 Map
核心在于维护一个 Map 实例,其键为请求的唯一标识(通常是 URL),值为正在进行的请求 Promise。初始化时只需创建一个空 Map,无需额外配置。每次调用封装好的请求函数时,先查询 Map 是否包含该 URL。如果包含,说明请求正在进行中,直接复用该 Promise;如果不包含,则执行实际的 fetch 操作,并将结果存入 Map。这种机制利用了 JavaScript 事件循环的特性,确保并发请求共享同一个异步任务。

const pendingRequests = new Map();
function fetchWithDedup(url, options = {}) {
if (pendingRequests.has(url)) {
return pendingRequests.get(url);
}
const promise = fetch(url, options)
.then(response => {
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return response.json();
})
.finally(() => {
pendingRequests.delete(url);
});
pendingRequests.set(url, promise);
return promise;
}上述代码展示了完整的实现逻辑。首先检查 pendingRequests 中是否存在 url。若存在,直接返回缓存的 Promise。若不存在,发起 fetch 请求,并在 finally 块中调用 delete 方法清理缓存。这样既实现了去重,又保证了请求完成后资源释放。测试时可多次快速调用同一 URL,观察网络面板只会发出一次请求,但所有调用者都会收到相同的响应数据。
立即学习“Java免费学习笔记(深入)”;
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型
如何处理请求失败后的重试与状态清理
当请求发生错误时,finally 钩子依然会执行,从而正确清除 Map 中的条目。这意味着如果第一次请求失败,后续的相同请求将视为新请求重新发起,而不是继续等待失败的 Promise。这种行为符合大多数业务场景的需求:失败后允许重试。如果需要区分成功和失败的状态,可以在 catch 中记录日志,但不影响 Map 的清理逻辑。此外,对于需要长期缓存且不过期的场景,可以修改 finally 的逻辑,仅在特定条件下删除键,或者引入过期时间机制,但基础的去重逻辑保持不变。

// 模拟多次并发调用
async function test() {
console.log('Start');
const p1 = fetchWithDedup('/api/data');
const p2 = fetchWithDedup('/api/data');
const p3 = fetchWithDedup('/api/other');
try {
const [res1, res2, res3] = await Promise.all([p1, p2, p3]);
console.log('Res1:', res1);
console.log('Res2:', res2); // 与 res1 相同
console.log('Res3:', res3);
} catch (e) {
console.error('Error:', e);
}
}
test();运行上述测试代码,控制台将打印三次日志,但网络请求仅发生两次:一次针对 /api/data,一次针对 /api/other。p1 和 p2 共享同一个 Promise 实例,因此它们解析出的值是相同的对象引用。这证明了去重机制在并发环境下有效工作。如果 /api/data 请求失败,p1 和 p2 都会抛出异常,而 /api/data 从 Map 中被移除,下次调用将发起新的请求。这种设计简洁且健壮,适用于大多数前端应用的数据获取场景。
基于 pendingRequests Map 中键值对的生命周期管理
在 fetchWithDedup 函数中,pendingRequests Map 的键是请求 URL,值是 Promise 对象。键的生命周期从请求发起开始,到请求结束(成功或失败)通过 finally 中的 delete 操作结束。值的生命周期与 Promise 一致,一旦 Promise settled(resolved 或 rejected),它就不再被引用,除非有其他变量持有引用。这种自动清理机制防止了内存泄漏,因为每个请求完成后,对应的键值对都会被移除。如果请求长时间挂起,键值对会一直保留在 Map 中,这可能是一个潜在问题,但在正常网络条件下,请求通常会在短时间内完成。对于极端情况,可以添加超时机制,在超时时强制删除键并拒绝 Promise,但这超出了基础去重的范畴。

















