前端缓存工具函数(含异步)

75 阅读2分钟

前言

前端经常要用到缓存,比如现在有个需求,每次重复调用接口(包括连续调用接口,第一个接口还未返回结果)都只执行一次。一般我们会做api级别的缓存,但是无法满足连续调用接口,第一个接口还未返回结果时,仍然只执行一次的要求。

实现

/** * 内存缓存,采用LRU算法 */
export default class LRUCache {
    // 缓存数据
    static cache = new Map()
    // 最大容量,容量小于等于0时,表示不使用内存缓存
    static capacity = 100

    // 设置缓存容量
    static setCapacity(capacity) {
        if (typeof capacity === 'number') {
            LRUCache.capacity = capacity
        }
    }

    // 从缓存中取值
    static get(key) {
        if (LRUCache.cache.has(key)) {
            const temp = LRUCache.cache.get(key)
            // 访问到的 key 若在缓存中,删除原来的值,放到尾部
            LRUCache.cache.delete(key)
            LRUCache.cache.set(key, temp)
            return typeof temp === 'object' ? JSON.parse(JSON.stringify(temp)) : temp
        }
        return ''
    }

    // 将值写入缓存
    static put(key, value) {
        // 容量小于等于0时,表示不使用内存缓存
        if (LRUCache.capacity <= 0) {
            return
        }
        if (LRUCache.cache.has(key)) {
            // 存在则删除
            LRUCache.cache.delete(key)
        } else if (LRUCache.cache.size >= LRUCache.capacity) {
            // 超过缓存长度,淘汰最近没使用的 第一个
            LRUCache.cache.delete(LRUCache.cache.keys().next().value)
            console.log(`LRUCache refresh: key:${key} , value:${value}`)
        }
        LRUCache.cache.set(key, typeof value === 'object' ? JSON.parse(JSON.stringify(value)) : value)
    }

    // 从缓存中移除
    static delete(key) {
        if (LRUCache.cache.has(key)) {
            // 存在则删除
            LRUCache.cache.delete(key)
        }
    }
    // 查看内存缓存
    static showCache() {
        console.log('LRUCache capacity:', LRUCache.capacity)
        console.log('LRUCache cache:', LRUCache.cache)
        // 打印出 Map 中的信息
        // LRUCache.cache.forEach((value, key) => console.log(`LRUCache.cache key: ${JSON.stringify(key)} ,value: ${JSON.stringify(value)}`))
    }
}

// 函数执行映射关系,执行完成后清除
const cacheKeyMap = new Map()

/**
 * @desc 缓存函数执行结果,防止重复执行某个函数
 * @param {*} func 待执行的函数
 * @param {*} cacheKey 缓存key,判断func是否重复的唯一标识,如果是接口请求,建议拼接请求参数标识唯一性
 * @param {*} cacheCondition 缓存条件函数,用于判断func结果是否存入缓存
 */export function cacheFuncResult(func, cacheKey, cacheCondition) {
    if (!func || typeof func !== 'function' || !cacheKey) {
        return Promise.reject()
    }

    // 先判断缓存里有没有
    const cacheResult = LRUCache.get(cacheKey)
    if (cacheResult) {
        return Promise.resolve(cacheResult)
    }

    // 判断执行映射关系里是否有当前函数在执行
    if (cacheKeyMap.has(cacheKey)) {
        return cacheKeyMap.get(cacheKey)
    }

    const promise = new Promise((resolve, reject) => {
        const result = func()
        if (typeof result.then === 'function') {
            result
                .then(res => {
                    // console.log('cacheFuncResult 异步函数执行成功:', res)
                    if (typeof cacheCondition === 'function' && cacheCondition(res)) {
                        // console.log('cacheFuncResult 存入缓存:', cacheKey)
                        LRUCache.put(cacheKey, res)
                    }
                    // 执行完成,清除执行映射关系
                    cacheKeyMap.delete(cacheKey)
                    resolve(res)
                })
                .catch(e => {
                    // 执行完成,清除执行映射关系
                    cacheKeyMap.delete(cacheKey)
                    reject(e)
                })
        } else {
            // console.log('cacheFuncResult 同步函数执行:')
            if (typeof cacheCondition === 'function' && cacheCondition(result)) {
                LRUCache.put(cacheKey, result)
            }
            // 执行完成,清除执行映射关系
            cacheKeyMap.delete(cacheKey)
            resolve(result)
        }
    })

    // 存入执行映射关系
    cacheKeyMap.set(cacheKey, promise)

    return promise
}