需求背景是这样的,我在开发的框架中有个枚举下拉组件,该组件会自动从服务端下载数据。当组件同时存在多个,比如表格渲染的场景,则在加载页面时候会瞬时集中重复请求。 考虑到该组件本身也有全局缓存数据的需求,同时框架的请求都是通过Promise实现,于是有了下面的解决方案
import { defineStore } from 'pinia'
import { post, CommItemVO } from '@/console/api/PubDataAPI'
export const usePubDataStore = defineStore('pub-data-store', () => {
const cache: Map<string, CommItemVO[]> = new Map()
/* 当同时多个发起相同的数据时,只请求一次 */
const waitingFlag: Map<string, boolean> = new Map()
const getSelectListData = (tag: string): Promise<CommItemVO[]> => {
const exists = cache.get(tag)
if (!exists) {
const isWaiting = waitingFlag.get(tag)
if (!isWaiting) {
waitingFlag.set(tag, true)
return post('api/console/select/list', { tag: tag }).then((data) => {
cache.set(tag, data)
waitingFlag.delete(tag)
return Promise.resolve(data)
})
} else {
return new Promise((resolve, reject) => {
//如果发现已经有在途查询,则等待,每100毫秒看下结果有没,有就继续
let retryCount = 0
const timer = setInterval(() => {
retryCount++
const cacheList = cache.get(tag)
if (!cacheList && retryCount > 30) {
clearInterval(timer)
reject()
}
if (cacheList) {
clearInterval(timer)
//返回数据
resolve(cacheList)
}
}, 100)
})
}
} else {
return Promise.resolve(exists)
}
}
return {
getSelectListData,
}
})
基本实现原理:
- 通过pinia实现全局缓存。
- 当有产生http请求的时候设置等待标志位
- 当其它请求数据调用并进来后,发现有在途的等待标志位,则发起一个新的Promise,这个Promise其实就是定时循环,一直等待第一个http请求完成后,提取它的返回结果,resolve给自己,本身并不真正发起http请求。
- 当然也有退出机制,加入第一个http请求失败了,排队等待的请求在重试30次后还未找到缓存也会退出。
我是handleRich (知乎四物番鸭咖啡),正在独立开发后台管理框架,过程中遇到的有趣问题和你们分享,欢迎加入讨论。SpringBoot、Vue3、Typescript、TDesign