函数缓存

一般可用于优化本地请求多次

// util.ts
import addSeconds from 'date-fns/add_seconds';
import getTime from 'date-fns/get_time';
import request from './ajax'; // 自己封装好的request

/**
 * 创建一个会缓存 func 结果的函数。 如果提供了 resolver ,就用 resolver 的返回值作为 key 缓存函数的结果。
 *  默认情况下用第一个参数作为缓存的 key。 func 在调用时 this 会绑定在缓存函数上。相比 lodash-es 库,增加了异步函数的支持。
 * @memberof module:shared
 * @param {function} fn 需要缓存化的函数
 * @param {function} opt 配置项 resolver: 生成 key , expire: 过期时间,以秒为单位
 * @return {function} 包装后的函数
 * @example
 *
 * const obj = { a: 1, b: 2 }
 * const foo = (obj) => Object.values(obj)
 * const bar = memorize(foo)
 * bar(obj)
 * // [1, 2]
 * obj.a = 10
 * bar(obj)
 * // [1, 2]
 */

function isPromise(value: any): boolean {
  return typeof value === 'object' && value !== null && typeof value.then === 'function';
}

export interface IMemorizeOptions {
  resolver?: (...value: any[]) => NonNullable<any>;
  expire: number;
}

export function memorize(
  fn: (...rest: any[]) => any,
  opt: IMemorizeOptions = {
    expire: 60
  }
) {
  const cache = new Map();
  const { resolver: hash, expire } = opt;

  function setExpire(time: number): number | null {
    return typeof time === 'number' ? getTime(addSeconds(Date.now(), time)) : null;
  }

  /* eslint-disable no-nested-ternary */
  function memoized(this: any, ...params: any[]) {
    const key = typeof hash === 'function' ? hash(...params) : params[0];
    const {
      expire: curExpire,
      value: memoizedValue,
      shouldReturnPromise
    }: {
      expire?: number;
      value?: any;
      shouldReturnPromise?: boolean;
    } = cache.get(key) || {};

    // 过期数据
    if (typeof curExpire === 'number' && Date.now() > curExpire) {
      cache.delete(key);
    }

    if (cache.has(key)) {
      return shouldReturnPromise ? Promise.resolve(memoizedValue) : memoizedValue;
    }

    const res = fn.apply(this, params);

    if (isPromise(res)) {
      return res.then(
        (value: any) => {
          cache.set(key, {
            value,
            expire: setExpire(expire),
            shouldReturnPromise: true
          });
          return value;
        },
        (err: Error) => Promise.reject(err)
      );
    }
    cache.set(key, {
      value: res,
      expire: setExpire(expire)
    });
    return res;
  }
  memoized.cache = cache;
  return memoized;
}

export function requestWithCache(options?: IMemorizeOptions) {
  return memorize(request, options);
}

export default requestWithCache;


// 使用

function memoReq() {
  return requestWithCache({
    resolver: (url: string, params?: any) => `${url}_${JSON.stringify(params)}`,
    expire: 60 * 10
  });
}

const cacheReq = memoReq();
// apply:
cacheReq('/v1/xxx.api').then().catch()

posted @ 2022-01-14 16:19  枫叶丶|  阅读(44)  评论(0编辑  收藏  举报