Skip to content

cachedAsync

为异步函数添加智能缓存,提升应用性能。

函数签名

typescript
function cachedAsync<T extends (...args: any[]) => Promise<any>>(
  fn: T,
  options?: CachedAsyncOptions
): CachedAsyncFunction<T>

interface CachedAsyncOptions {
  keyGenerator?: (...args: any[]) => string
  ttl?: number
  maxSize?: number
  cache?: LRUCache<string, any>
  cacheErrors?: boolean
  debug?: boolean
  console?: { log: (...args: any[]) => void; warn: (...args: any[]) => void }
}

interface CachedAsyncFunction<T> {
  (...args: Parameters<T>): ReturnType<T>
  clear: () => void
  delete: (...args: Parameters<T>) => void
  has: (...args: Parameters<T>) => boolean
  getStats: () => { size: number; hits: number; misses: number; hitRate: number }
}

参数

参数名类型必填说明
fnAsyncFunction需要缓存的异步函数
options.keyGeneratorFunction缓存键生成函数,默认使用 JSON.stringify
options.ttlnumber缓存过期时间(毫秒),0表示永不过期
options.maxSizenumber最大缓存容量,默认500
options.cacheLRUCache自定义缓存实例
options.cacheErrorsboolean是否缓存错误结果,默认false
options.debugboolean是否启用调试日志,默认false

返回值

类型说明
CachedAsyncFunction带缓存的异步函数,附带缓存管理方法

工作原理

  1. 首次调用:执行异步函数,缓存结果
  2. 缓存命中:直接返回缓存结果,不执行函数
  3. TTL 过期:缓存过期后重新执行函数
  4. LRU 清理:容量满时删除最少使用的缓存
  5. 防重复请求:并发相同请求共享 Promise

基础示例

typescript
import { cachedAsync } from 'zcw-shared/functions/async/cachedAsync'

// 创建带缓存的异步函数
const fetchUser = cachedAsync(
  async (userId: string) => {
    const response = await fetch(`/api/users/${userId}`)
    return response.json()
  },
  {
    ttl: 5000,      // 5秒过期
    maxSize: 100    // 最多100个
  }
)

// 第一次调用 - 执行函数并缓存
const user1 = await fetchUser('user-123')  // 发起网络请求

// 第二次调用 - 使用缓存
const user2 = await fetchUser('user-123')  // 立即返回,无网络请求

// 5秒后缓存过期,再次调用会重新请求
await new Promise(r => setTimeout(r, 5000))
const user3 = await fetchUser('user-123')  // 重新发起网络请求

实际应用场景

API 请求缓存

typescript
// 用户信息请求
const fetchUser = cachedAsync(
  async (userId: string) => {
    const res = await fetch(`/api/users/${userId}`)
    return res.json()
  },
  { ttl: 60000 }  // 1分钟缓存
)

// 文章列表请求
const fetchPosts = cachedAsync(
  async (page: number, pageSize: number) => {
    const res = await fetch(`/api/posts?page=${page}&size=${pageSize}`)
    return res.json()
  },
  { ttl: 30000 }  // 30秒缓存
)

// 使用
app.get('/users/:id', async (req, res) => {
  const user = await fetchUser(req.params.id)
  res.json(user)
})

数据库查询缓存

typescript
const getUserById = cachedAsync(
  async (userId: string) => {
    return await db.users.findOne({ id: userId })
  },
  {
    ttl: 10000,     // 10秒缓存
    maxSize: 1000   // 最多缓存1000个用户
  }
)

// 第一次查询数据库
const user1 = await getUserById('123')

// 后续查询使用缓存,不访问数据库
const user2 = await getUserById('123')

共享缓存实例

typescript
import { LRUCache } from 'zcw-shared/functions/data-structures/LRUCache'
import { createCachedAsyncWithCache } from 'zcw-shared/functions/async/cachedAsync'

// 创建共享的缓存实例
const sharedCache = new LRUCache<string, any>({ 
  capacity: 1000,
  debug: true,
  console: { log: console.log, warn: console.warn }
})

// 创建工厂函数
const createCachedFn = createCachedAsyncWithCache(sharedCache)

// 多个函数共享同一个缓存(注意键冲突)
const fetchUser = createCachedFn(
  async (id: string) => fetch(`/api/users/${id}`).then(r => r.json()),
  { ttl: 60000, keyGenerator: (id) => `user:${id}` }
)

const fetchPost = createCachedFn(
  async (id: string) => fetch(`/api/posts/${id}`).then(r => r.json()),
  { ttl: 30000, keyGenerator: (id) => `post:${id}` }
)

防止重复请求

typescript
const fetchData = cachedAsync(
  async (id: string) => {
    // 模拟慢速 API
    await new Promise(r => setTimeout(r, 2000))
    return fetch(`/api/data/${id}`).then(r => r.json())
  }
)

// 并发请求
const [result1, result2, result3] = await Promise.all([
  fetchData('123'),  // 发起请求
  fetchData('123'),  // 复用第一个请求的 Promise
  fetchData('123')   // 复用第一个请求的 Promise
])

// 只发起了1次网络请求
console.log(result1 === result2)  // true

自定义缓存键

typescript
// 复杂参数的缓存键生成
const searchProducts = cachedAsync(
  async (query: string, filters: any, sort: string) => {
    return fetch('/api/products/search', {
      method: 'POST',
      body: JSON.stringify({ query, filters, sort })
    }).then(r => r.json())
  },
  {
    keyGenerator: (query, filters, sort) => {
      // 自定义键生成逻辑
      return `search:${query}:${JSON.stringify(filters)}:${sort}`
    }
  }
)

缓存管理

typescript
const fetchUser = cachedAsync(async (id) => { ... }, options)

// 清除所有缓存
fetchUser.clear()

// 删除特定缓存
fetchUser.delete('user-123')

// 检查缓存是否存在
if (fetchUser.has('user-123')) {
  console.log('缓存存在')
}

// 获取统计信息
const stats = fetchUser.getStats()
console.log(stats)
// {
//   size: 10,        // 当前缓存数量
//   hits: 100,       // 命中次数
//   misses: 20,      // 未命中次数
//   hitRate: 0.83    // 命中率 83%
// }

错误处理

typescript
// 默认不缓存错误
const fetchUser = cachedAsync(async (id) => {
  const res = await fetch(`/api/users/${id}`)
  if (!res.ok) throw new Error('User not found')
  return res.json()
})

try {
  await fetchUser('invalid-id')  // 抛出错误
} catch (e) {
  // 错误不会被缓存
}

await fetchUser('invalid-id')  // 再次请求,不使用缓存

// 启用错误缓存
const fetchUserWithErrorCache = cachedAsync(
  async (id) => { ... },
  { cacheErrors: true, ttl: 5000 }
)

// 错误会被缓存5秒
try {
  await fetchUserWithErrorCache('invalid-id')
} catch (e) {
  // 错误被缓存
}

await fetchUserWithErrorCache('invalid-id')  // 立即抛出缓存的错误

带缓存的异步函数,轻松提升应用性能!