SWR(Stale While Revalidate) 是指在进行数据请求的时候优先检查缓存数据,若数据尚在有效期则直接返回数据。若没有缓存数据或者数据过期则再去请求接口,接口返回值返回后再将其存到缓存中。
这里我们就涉及到几个部分:
const cache = new Map()
async function swr(cacheKey: string, fetcher: Fetcher, cacheTime: number) { const data = cache.get(cacheKey) || { value: null, time: 0, promise: null } cache.set(cacheKey, data)
const isStaled = Date.now() - data.time >= cacheTime
if (isStaled && !data.promise) {
data.promise = fetcher()
.then((val) => {
data.value = val
data.time = Date.now()
})
.catch(err => {
console.log(err)
})
.finally(() => {
data.promise = null
})
}
if (data.promise && !data.value) await data.promise return data.value }
这里,我们使用 Date.now() 来获取当前的时间戳,计算过后来与输入的 cacheTime 来进行对比。然后附加对 data.promise 的判断来进行数据的获取。<a name="jnX4r"></a>### 更新缓存算法针对于缓存算法,上一个例子我们只是对数据进行了一个简单的 map 缓存,这对于实际使用是不满足的。在实际使用中,我们还需要考虑缓存的数据量以及数据优先情况。这里我们可以封装一个简单的 LRU 算法。```typescriptclass LRUCache {public capacity: numberpublic cache: Map<any, any>constructor(capacity: number) {this.capacity = capacitythis.cache = new Map()}get(key: any) {if (this.cache.has(key)) {const temp = this.cache.get(key)this.cache.delete(key)this.cache.set(key, temp)return temp}return undefined}set(key: any, val: any) {if (this.cache.has(key)) {// 先删除再加入this.cache.delete(key)} else if (this.cache.size >= this.capacity) {// 删除首值,即最不常用的值this.cache.delete(this.cache.keys().next().value)}this.cache.set(key, val)}}
添加条件运算
type ShouldCache = () => any | string | undefinedasync function swr(cacheKey: ShouldCache) {// ...const cKey = typeof cacheKey === 'function' ? cacheKey() : cacheKeyif (cKey) {// ...} else {return fetcher()}}
根据 cacheKey 的值来进行判断,看是否需要使用缓存。
组合
type Fetcher = () => Promise<any>type shouldCache = () => any | string | undefined// LRU cacheexport class LRUCache {public capacity: numberpublic cache: Map<any, any>constructor(capacity: number) {this.capacity = capacitythis.cache = new Map()}get(key: any) {if (this.cache.has(key)) {const temp = this.cache.get(key)this.cache.delete(key)this.cache.set(key, temp)return temp}return undefined}set(key: any, val: any) {if (this.cache.has(key)) {this.cache.delete(key)} else if (this.cache.size >= this.capacity)this.cache.delete(this.cache.keys().next().value)this.cache.set(key, val)}}const cache = new LRUCache(50)export async function swr(cacheKey: shouldCache,fetcher: Fetcher,cacheTime: number) {const cKey = typeof cacheKey === 'function' ? cacheKey() : cacheKeyif (cKey) {const data = cache.get(cacheKey) || { value: null, time: 0, promise: null }cache.set(cKey, data)const isStaled = Date.now() - data.time > cacheTimeif (isStaled && !data.promise) {data.promise = fetcher().then((val) => {data.value = valdata.time = Date.now()}).catch((err) => {console.error(err)}).finally(() => {data.promise = null})}if (data.promise && !data.value) await data.promisereturn data.value} else {return fetcher()}}
这样我们就完成了一个简单的 SWR 请求函数。
