CacheRequest.js 新增一种缓存模式

/*
 * 缓存并发接口函数,解决重复请求和并发请求问题
 * id:唯一值
 * cacheSecond:缓存时间,单位秒
 * syncFunc:加载函数
 * cacheMode:缓存模式,'default' 过期后重新拉取;'stale' 过期先用旧缓存返回、后台刷新(默认 'default')
 */
const _map = {}
const _startTimeMap = {}
const _idArr = []
const _resArr = []
const _timeArr = []
let len = 0
const lenMax = 3000

async function CacheRequest(syncFunc, id, cacheSecond = 0, cacheMode = 'default') {
  const now = Date.now()
  const idx = _idArr.indexOf(id)
  if (idx > -1) {
    // 在有效时间
    if (now < _timeArr[idx]) {
      return _resArr[idx]
    } else if (cacheMode === 'stale') {
      // 过期但启用"先用旧缓存、后台更新"模式:立即返回旧值,并触发一次后台刷新
      _refreshCache(syncFunc, id, cacheSecond)
      return _resArr[idx]
    } else {
      _idArr.splice(idx, 1)
      _resArr.splice(idx, 1)
      _timeArr.splice(idx, 1)
      if (len > 0) {
        len--
      } else {
        len = lenMax
      }
    }
  }
  // 兼容并发加载的情况
  if (!_map[id]) {
    _map[id] = []
    _startTimeMap[id] = now
    const res = await syncFunc()
    // 缓存时间
    if (cacheSecond > 0) {
      _idArr[len] = id
      _resArr[len] = res
      _timeArr[len] = cacheSecond * 1000 + Date.now()
      len++
      if (len > lenMax) {
        len = 0
      }
    }
    setTimeout(function () {
      if (_map[id].length > 0) {
        _map[id].forEach(function (callback) {
          callback(res)
        })
      }
      delete _map[id]
      delete _startTimeMap[id]
    }, 0)
    return res
  } else {
    // 10 秒超时后,并发不做优化
    if (now - _startTimeMap[id] < 10000) {
      return new Promise(function (resolve) {
        _map[id].push(resolve)
      })
    } else {
      return syncFunc()
    }
  }
}

/*
 * 后台刷新缓存(cacheMode = 'stale' 模式专用)
 * 成功:更新缓存内容和缓存时间;失败:保留旧缓存,等待下次请求重试
 * 并发去重:同一 id 已有刷新进行中则跳过;超过 10 秒未完成允许重新触发
 */
function _refreshCache(syncFunc, id, cacheSecond) {
  if (_map[id]) {
    if (Date.now() - _startTimeMap[id] < 10000) {
      return
    }
    // 上一次刷新挂起超过 10 秒,允许并发触发新的刷新
    delete _map[id]
    delete _startTimeMap[id]
  }
  const callbacks = []
  _map[id] = callbacks
  _startTimeMap[id] = Date.now()
  ;(async function () {
    try {
      const res = await syncFunc()
      if (cacheSecond > 0) {
        const idx = _idArr.indexOf(id)
        if (idx > -1) {
          // 更新已有缓存
          _resArr[idx] = res
          _timeArr[idx] = cacheSecond * 1000 + Date.now()
        } else {
          // 缓存槽已被覆盖/移除,重新写入
          _idArr[len] = id
          _resArr[len] = res
          _timeArr[len] = cacheSecond * 1000 + Date.now()
          len++
          if (len > lenMax) {
            len = 0
          }
        }
      }
    } catch (e) {
      // 后台刷新失败:保留旧缓存,等待下次请求重试
    } finally {
      // 只清理自己发起的刷新,避免误删后续重新触发的刷新状态
      if (_map[id] === callbacks) {
        delete _map[id]
        delete _startTimeMap[id]
      }
    }
  })()
}

export default CacheRequest

 

posted @ 2026-10-23 10:10  无工时代  阅读(3)  评论(0)    收藏  举报