HstarDoc icon indicating copy to clipboard operation
HstarDoc copied to clipboard

一个缓存异步函数返回值的方法

Open hstarorg opened this issue 2 years ago • 0 comments

V1 版本:

import { EventEmitter } from "events";
const bus = new EventEmitter();
// 缓存队列
const cacheMap: Map<
  string,
  {
    status: "pending" | "loading" | "succeed" | "failed";
    data?: any;
    queue: string[];
  }
> = new Map();

async function asyncCache(cacheKey: string, asyncFn: () => Promise<any>) {
  // 先初始化缓存
  if (!cacheMap.has(cacheKey)) {
    cacheMap.set(cacheKey, { status: "pending", queue: [] });
  }
  const fnId = Math.random().toString(16).slice(2);
  // 缓存对象,可直接修改对象属性
  let cacheItem = cacheMap.get(cacheKey);
  // 有缓存成功的数据,直接返回
  if (cacheItem.status === "succeed") {
    return cacheItem.data!;
  }

  // 加入队列
  cacheItem.queue.unshift(fnId);

  // 有请求中的数据,先加入队列
  if (cacheItem.status === "loading") {
    await new Promise((resolve) => {
      bus.on("ASYNC_DONE", () => {
        // 如果数据缓存成功或者队列中轮到自己,就停止等待
        if (
          cacheItem.status === "succeed" ||
          fnId === cacheItem.queue[cacheItem.queue.length - 1]
        ) {
          resolve(1);
        }
      });
    });
  }
  cacheItem = cacheMap.get(cacheKey);
  // 有缓存成功的数据,直接返回
  if (cacheItem.status === "succeed") {
    return cacheItem.data!;
  }
  cacheItem.status = "loading";
  return Promise.resolve()
    .then(() => {
      return asyncFn();
    })
    .then((data) => {
      // 记录缓存状态和缓存数据
      cacheItem.status = "succeed";
      cacheItem.data = data;
      return data;
    })
    .catch((reason) => {
      cacheItem.status = "failed";
      return Promise.reject(reason);
    })
    .finally(() => {
      // 结束了自己出队
      cacheItem.queue.pop();
      // 通知其他等待的请求
      bus.emit("ASYNC_DONE");
    });
}

hstarorg avatar Feb 27 '23 07:02 hstarorg