标准答案

  1. 第一次请求创建并保存 Promise,后续请求直接返回同一 Promise。
  2. 在 finally 中删除 in-flight 记录;结果缓存和请求合并是两个不同层次。
  3. 跨进程或多实例场景需要共享锁、缓存或队列,并处理持有者失效和超时。

题目解析

请求合并把同一实例内相同 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 必须覆盖会影响结果的身份、版本和查询条件。

作者信息