标准答案
- 先为请求生成稳定 cacheKey,通常由 url、method、params、body 中的关键字段组成。
- 用 pendingMap 保存进行中的请求,相同 key 直接返回已有 Promise。
- 用 cacheMap 保存成功结果和过期时间,命中未过期缓存时直接返回数据。
- 请求完成后要从 pendingMap 删除,避免长期占用内存。
- 实际项目还要考虑错误是否缓存、手动失效、鉴权维度、POST 是否允许缓存和数据一致性。
题目解析
去重和缓存不是一回事。去重解决的是“同一时刻重复请求”,缓存解决的是“短时间内重复读取同一结果”。两者可以组合,但状态要分开管理。
cacheKey 设计很关键。如果 key 太粗,会把不同用户、不同参数的请求错误合并;如果 key 太细,又无法命中缓存。
失败请求通常不应该默认缓存,否则一次临时网络错误会在有效期内持续返回失败。进行中的请求无论成功失败都要清理 pendingMap。
代码示例
下面的模块同时支持进行中请求去重和成功结果 TTL 缓存。
JavaScript
function createRequestCache({ ttl = 30000 } = {}) {
const pendingMap = new Map()
const cacheMap = new Map()
function request(key, fetcher) {
const cached = cacheMap.get(key)
const now = Date.now()
if (cached && cached.expireAt > now) {
return Promise.resolve(cached.data)
}
if (pendingMap.has(key)) {
return pendingMap.get(key)
}
const task = Promise.resolve()
.then(fetcher)
.then(data => {
cacheMap.set(key, {
data,
expireAt: Date.now() + ttl
})
return data
})
.finally(() => {
pendingMap.delete(key)
})
pendingMap.set(key, task)
return task
}
function invalidate(key) {
cacheMap.delete(key)
}
return {
request,
invalidate
}
}常见误区
- 只做结果缓存,没有复用进行中的 Promise,首屏多个组件仍然会同时发重复请求。
- cacheKey 没有包含用户、参数或请求体,导致不同请求拿到同一份缓存。
- 请求失败后没有清理 pendingMap,后续请求一直复用失败状态或卡住。