标准答案
- 第一次请求创建并保存 Promise,后续请求直接返回同一 Promise。
- 在 finally 中删除 in-flight 记录;结果缓存和请求合并是两个不同层次。
- 跨进程或多实例场景需要共享锁、缓存或队列,并处理持有者失效和超时。
题目解析
请求合并把同一实例内相同 Key 的并发加载绑定到同一个 Promise,减少重复回源和热点重建。它只存在于当前进程,进程重启或多实例部署仍可能同时执行。
in-flight 记录必须在成功、失败和取消时清理,否则一次失败会污染后续请求,或一个永远不结束的 Promise 会占用 Map。Key 还必须包含租户、权限和查询版本边界。
跨进程要使用共享锁、分布式请求合并或队列,但这些机制仍不能替代业务幂等。结果缓存、请求合并和最终事实写入应分别说明生命周期。
代码示例
单实例请求合并:
TypeScript
const inFlight = new Map<string, Promise<Result>>()
function load(key: string) {
const existing = inFlight.get(key)
if (existing) return existing
const task = fetchData(key).finally(() => inFlight.delete(key))
inFlight.set(key, task)
return task
}常见误区
- 误区:失败后不删除 Promise。改正:使用 finally 清理 in-flight 记录,并为长任务设置超时。
- 误区:把请求合并当作持久化幂等。改正:需要跨实例和重启保护时,使用持久化幂等记录或唯一约束。
- 误区:Key 没有包含租户或权限边界。改正:Key 必须覆盖会影响结果的身份、版本和查询条件。