You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

TypeScript中如何为存在类型的数据加载逻辑添加记忆化缓存?

解决方案

现有ID方案的类型修复

你给DataLoader加ID的思路本身是合理的,只需要调整缓存类型和增加安全的类型断言即可,完全不需要使用any:

async function runValidation() {
  // 缓存值用unknown类型,比any安全,不允许随意操作
  const memo = new Map<string, unknown>();
  for (const validator of validators) {
    // 回调内的泛型参数A对应当前校验器的加载器返回值类型
    await validator(async <A>([dataLoader, check]: Validator<A>) => {
      let cached = memo.get(dataLoader.id);
      if (!cached) {
        cached = await dataLoader.load();
        memo.set(dataLoader.id, cached);
      }
      // 断言安全:只要你保证相同ID的DataLoader返回值类型完全一致,就不会有类型错误
      return check(cached as A);
    });
  }
}

优化点:缓存Promise避免重复并发请求

你当前的实现存在一个小漏洞:如果同一个ID的加载器第一次请求还未返回,第二个同ID的请求进来时,还是会触发第二次重复加载。可以将缓存改为存储Promise,解决这个问题:

async function runValidation() {
  const memo = new Map<string, Promise<unknown>>();
  for (const validator of validators) {
    await validator(async <A>([dataLoader, check]: Validator<A>) => {
      let cachedPromise = memo.get(dataLoader.id);
      if (!cachedPromise) {
        cachedPromise = dataLoader.load();
        memo.set(dataLoader.id, cachedPromise);
      }
      const cached = await cachedPromise;
      return check(cached as A);
    });
  }
}

替代方案:无需ID的WeakMap实现

如果你的相同加载逻辑都会复用同一个DataLoader实例,不需要跨实例共享缓存,可以去掉ID字段,直接用WeakMap以加载器实例为键存储缓存,无需人工维护ID,还能自动触发垃圾回收,避免内存泄漏:

// 调整类型,去掉id字段
type DataLoader<A> = {
  load: () => Promise<A>
}
type Validator<A> = [dataLoader: DataLoader<A>, check: (input: A) => void];
type ExistentialValidator = <R>(run: <A>(validator: Validator<A>) => R) => R;
declare const validators: ExistentialValidator[];

// 运行逻辑
async function runValidation() {
  const memo = new WeakMap<DataLoader<any>, Promise<unknown>>();
  for (const validator of validators) {
    await validator(async <A>([dataLoader, check]: Validator<A>) => {
      let cachedPromise = memo.get(dataLoader);
      if (!cachedPromise) {
        cachedPromise = dataLoader.load();
        memo.set(dataLoader, cachedPromise);
      }
      const cached = await cachedPromise;
      return check(cached as A);
    });
  }
}

两种方案选型建议

  • 选ID方案:如果你需要跨不同DataLoader实例共享缓存(比如两个不同实例加载完全相同的资源),选择带ID的方案,仅需要做好ID的唯一性管控即可。
  • 选WeakMap方案:如果你相同的加载逻辑都会复用同一个DataLoader实例,选择WeakMap方案,不需要维护ID,也不会有ID冲突的风险,内存占用更优。

内容的提问来源于stack exchange,提问作者ksaveljev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.06 21:18:00