多个重复请求合成一个

355 阅读2分钟

多个重复请求合成一个

背景:

抽离的子组件多次渲染会重复请求,API是放在这个子组件内的(因为API可复用)。

思路:

  1. 用闭包的方式返回一个新方法,同时设置唯一key
  2. 用抢占的方式缓存promise,后调用的直接返回缓存
  3. 成功后清除缓存

带注释版本

/**
 * 单例 promise版
 * @param p
 * @param getKey 从参数里取一个直用于缓存该请求是否合并
 * @example
 * const getProjectSingle =  singlePromise(getProject, (projectId) => projectId)
 * /// 3合一
 * getProjectSingle('1');
 * getProjectSingle('1');
 * getProjectSingle('1');
 * /// 参数不一致,会发出
 * getProjectSingle('2');
 */
export function singlePromise(p, getKey: (...arg) => string | number) {
  // 用于标记是否在请求中,key:请求中的promise
  const cache = {}
  return (...arg) => {
    // 因为不同参数请求结果不一样,所以需要根据参数设置一个唯一key,这个函数由业务实现
    const key = getKey(...arg)

    // 存在说明已经被抢先发出请求了,返回这个promise
    if (cache[key]) {
      return cache[key]
    }
    // 第一个promise,结束后需要清除自己
    cache[key] = p(...arg).finally(() => {
      delete cache[key]
    })
    // 第一个promise返回自己的promise
    return cache[key]
  }
}

TS版本

/**
 * 单例 promise版
 * @param p
 * @param getKey 从参数里取一个直用于缓存该请求是否合并
 * @example
 * const getProjectSingle =  singlePromise(getProject, (projectId) => projectId)
 * /// 3合一
 * getProjectSingle('1');
 * getProjectSingle('1');
 * getProjectSingle('1');
 * /// 参数不一致,会发出
 * getProjectSingle('2');
 */
export function singlePromise<T extends (...arg: any[]) => Promise<any>>(p: T, getKey: (...arg: Parameters<T>) => string | number) {
  const cache: Record<string, Promise<any> | undefined> = {}
  return (...arg: Parameters<T>): ReturnType<T> => {
    const key = getKey(...arg)
    if (cache[key]) {
      return cache[key] as ReturnType<T>
    }
    cache[key] = p(...arg).finally(() => {
      delete cache[key]
    })
    return cache[key] as ReturnType<T>
  }
}

TS版本效果

Kapture 2022-12-12 at 18.06.42.gif

其中

T extends (...arg: any[]) => Promise<any> 是为了自动推导参数类型
Parameters<T> 是为了从T推导参数类型,方便写getKey
ReturnType<T> 获取原请求方法的返回类型

补充

如果想实现过期时间,则需在第二步多一个判断,Date.now() - expire > 0


下面尝试一下ChatGPT能不能写出来

image.png

image.png

迭代了9次,这个思路是对的,但是没办法写出一个直接能用的,而且网络经常断开,算了就这样吧 image.png