多个重复请求合成一个
背景:
抽离的子组件多次渲染会重复请求,API是放在这个子组件内的(因为API可复用)。
思路:
- 用闭包的方式返回一个新方法,同时设置唯一key
- 用抢占的方式缓存promise,后调用的直接返回缓存
- 成功后清除缓存
带注释版本
/**
* 单例 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版本效果
其中
T extends (...arg: any[]) => Promise<any> 是为了自动推导参数类型
Parameters<T> 是为了从T推导参数类型,方便写getKey
ReturnType<T> 获取原请求方法的返回类型
补充
如果想实现过期时间,则需在第二步多一个判断,Date.now() - expire > 0
下面尝试一下ChatGPT能不能写出来
迭代了9次,这个思路是对的,但是没办法写出一个直接能用的,而且网络经常断开,算了就这样吧