当前位置: 首页 > article >正文

简单的接口缓存机制,避免了重复请求,同时支持缓存过期时间。

简单的接口缓存机制,避免了重复请求,同时支持缓存过期时间。

const CACHE_LIFETIME = 30

interface ApiCacheOptions {
  /** 缓存时长(秒) */
  cacheLifetime?: number
}

type CacheStatus = 'notStarted' | 'loading' | 'finished' | 'error'

interface CacheItem<T = any> {
  status: CacheStatus
  result: T | null
  requestList: ((res: T | Error) => void)[]
  timer?: NodeJS.Timeout
}

const resultCache = new Map<string, CacheItem>()

export async function apiCache<T>(
  apiKey: string,
  func: () => Promise<T>,
  options?: ApiCacheOptions
): Promise<T> {
  const cacheLifetime = options?.cacheLifetime ?? CACHE_LIFETIME
  const item = getItem<T>(apiKey)

  if (item.status === 'finished') {
    return item.result as T
  }

  if (item.status === 'loading') {
    return new Promise<T>((resolve, reject) => {
      addSubscriber(apiKey, (res) => {
        if (res instanceof Error) reject(res)
        else resolve(res)
      })
    })
  }

  try {
    item.status = 'loading'
    item.result = await func()
    item.status = 'finished'

    // 设置缓存过期
    item.timer = setTimeout(() => {
      removeItem(apiKey)
    }, cacheLifetime * 1000)

    onAccessTokenFetched(apiKey, item.result)
    return item.result
  } catch (error) {
    item.status = 'error'
    onAccessTokenFetched(apiKey, error as Error) // 传递错误给订阅者
    throw error
  }
}

function getItem<T>(key: string): CacheItem<T> {
  if (!resultCache.has(key)) {
    resultCache.set(key, { status: 'notStarted', result: null, requestList: [] })
  }
  return resultCache.get(key) as CacheItem<T>
}

function removeItem(key: string) {
  if (resultCache.has(key)) {
    const item = resultCache.get(key)
    if (item?.timer) clearTimeout(item.timer) // 清理定时器
    resultCache.delete(key) // 彻底删除,防止内存泄漏
  }
}

function addSubscriber<T>(key: string, callback: (res: T | Error) => void) {
  const item = getItem<T>(key)
  item.requestList.push(callback)
}

function onAccessTokenFetched<T>(key: string, result: T | Error) {
  const item = getItem<T>(key)
  item.requestList.forEach((callback) => callback(result))
  item.requestList = [] // 清空请求列表
}

http://www.kler.cn/a/555441.html

相关文章:

  • Android 利用addr2line 定位 native crash问题
  • 【Python】迭代器与生成器详解,附代码(可迭代对象、定义、实现方式、区别、使用场景)
  • Dify的安装(本地部署deepseek)
  • 【核心算法篇十九】《 DeepSeek因果推断:双重差分模型如何破解政策评估的「时空难题」》
  • QSNCTF做题记录-应急响应
  • Hot100 贪心算法
  • 撕碎QT面具(9):QT创建和清除Qchart内容的办法
  • Spring AI集成Ollama调用本地大模型DeepSeek
  • 接口测试工具:Postman
  • 算法-字符串篇01-反转字符串
  • 共同性思考:数据标注研究与数据标注工作者 工作范式思考
  • Ubuntu22.04 - gflags的安装和使用
  • 七星棋牌全开源修复版源码解析:6端兼容,200种玩法全面支持
  • Linux 性能调优简单指南
  • Java+SpringBoot+Vue+数据可视化的综合健身管理平台(程序+论文+讲解+安装+调试+售后)
  • ELK 日志收集框架搭建
  • docker从容器中cp到本地、cp本地到容器
  • PHP 数据库操作:以 MySQL 为例
  • 使用Python进行PDF隐私信息检测
  • 美团MTSQL特性解析:技术深度与应用广度的完美结合