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 算法。
```typescript
class LRUCache {
public capacity: number
public cache: Map<any, any>
constructor(capacity: number) {
this.capacity = capacity
this.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 | undefined
async function swr(cacheKey: ShouldCache) {
// ...
const cKey = typeof cacheKey === 'function' ? cacheKey() : cacheKey
if (cKey) {
// ...
} else {
return fetcher()
}
}
根据 cacheKey 的值来进行判断,看是否需要使用缓存。
组合
type Fetcher = () => Promise<any>
type shouldCache = () => any | string | undefined
// LRU cache
export class LRUCache {
public capacity: number
public cache: Map<any, any>
constructor(capacity: number) {
this.capacity = capacity
this.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() : cacheKey
if (cKey) {
const data = cache.get(cacheKey) || { value: null, time: 0, promise: null }
cache.set(cKey, 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.error(err)
})
.finally(() => {
data.promise = null
})
}
if (data.promise && !data.value) await data.promise
return data.value
} else {
return fetcher()
}
}
这样我们就完成了一个简单的 SWR 请求函数。