阿里前端笔试失败复盘:设计接口请求缓存函数

166 阅读3分钟

前言

今天刚做了阿里社招的笔试题,印象最深刻的就是这道“设计接口请求缓存函数”的题目,由于平时业务中没有遇到过这个需求,思路一下子也运转不过来,很可惜的没有回答好。

笔试失败虽然很失落,但是也是一个很好的学习机会,借此文重新梳理一下解题思路和代码实现。

题目描述

由于笔试过程有点紧张,忘记把题目记下来了,自己重新组织了一下

  1. 缓存请求的结果,同一个请求多次访问不会多次请求服务器,而是返回前端缓存的结果
  2. 同时发多个相同的请求,如果第一个请求成功,那么剩余的请求都不会发出,成功的结果作为剩余请求返回

实现请求缓存功能

使用 Map 数据结构存储缓存数据

const cache = new Map();

根据接口地址和参数生成一个唯一的 Key 用于存取缓存数据

const genKey = ({ api, params }) => {
  return `${api}_${JSON.stringify(params)}`;
};

使用 fetch 封装请求函数,如果使用 axios 或者其它库,则无需自主封装。这里为了尽量减少三方库的引入,所以使用原生 api 来实现

let apiUrl = api;

// 如果是 GET 请求并且有参数,将参数附加到 URL 上
if (method === "GET" && params) {
  const queryString = Object.keys(params)
    .map((key) => `${key}=${encodeURIComponent(params[key])}`)
    .join("&");
  apiUrl += `?${queryString}`;
}

const requestOptions = {
  method: method,
  headers: {
    "Content-Type": "application/json",
  },
};

if (method === "POST") {
  requestOptions.body = JSON.stringify(params);
}

return fetch(apiUrl, requestOptions)
  .then((response) => {
    if (!response.ok) {
      throw new Error("Network response was not ok");
    }
    console.log("正在请求接口...");
    const res = response.json();
    cache.set(cacheKey, res);
    return res;
  })
  .catch((error) => {
    console.log("正在请求接口...");
    console.error("Error:", error);
    throw error;
  });

执行效果如下图:

image.png

实现请求并发功能

声明一个存放等待状态的回调函数的变量callbackMap

const callback = new Map();

声明一个存缓存结果的变量statusMap

const statusMap = new Map();

判断是否处于并行状态,并行状态则将函数放入请求队列中,等待执行完成后回调

if (statusMap.has(cacheKey)) {
  const curStatus = statusMap.get(cacheKey);

  // 判断当前的接口缓存状态,如果是 complete ,则代表已有缓存
  if (curStatus === "complete") {
    console.log("正在读取缓存...");
    return Promise.resolve(cache.get(cacheKey));
  }

  // 如果是 pending ,则代表正在请求中,这里放入回调函数
  if (curStatus === "pending") {
    console.log("有一个相同的请求正在处理中...");
    return new Promise((resolve, reject) => {
      if (callbackMap.has(cacheKey)) {
        callbackMap.get(cacheKey).push({
          onSuccess: resolve,
          onError: reject,
        });
      } else {
        callbackMap.set(cacheKey, [
          {
            onSuccess: resolve,
            onError: reject,
          },
        ]);
      }
    });
  }
}

statusMap.set(cacheKey, "pending");

执行回调

return fetch(apiUrl, requestOptions)
  .then((response) => {
    if (!response.ok) {
      throw new Error("Network response was not ok");
    }
    console.log("正在请求接口...");
    const res = response.json();
    statusMap.set(cacheKey, "complete");
    cache.set(cacheKey, res);

    if (callbackMap.has(cacheKey)) {
      callbackMap.get(cacheKey).forEach((callback) => {
        console.log('正在执行回调函数')
        callback.onSuccess(res);
      });
      // 调用完成之后清掉,用不到了
      callbackMap.delete(cacheKey);
    }

    return res;
  })
  .catch((error) => {
    console.log("正在请求接口...");
    statusMap.delete(cacheKey);
    console.error("Error:", error);
    throw error;
  });

执行效果如下图:

image.png

总结

这道题的核心就是要解决缓存和并行执行两个问题,缓存就是将数据放在缓存池子中,按需存取即可,但是并行的话需要有回调函数存取的意识才能更好的实现。