前言
今天刚做了阿里社招的笔试题,印象最深刻的就是这道“设计接口请求缓存函数”的题目,由于平时业务中没有遇到过这个需求,思路一下子也运转不过来,很可惜的没有回答好。
笔试失败虽然很失落,但是也是一个很好的学习机会,借此文重新梳理一下解题思路和代码实现。
题目描述
由于笔试过程有点紧张,忘记把题目记下来了,自己重新组织了一下
- 缓存请求的结果,同一个请求多次访问不会多次请求服务器,而是返回前端缓存的结果
- 同时发多个相同的请求,如果第一个请求成功,那么剩余的请求都不会发出,成功的结果作为剩余请求返回
实现请求缓存功能
使用 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;
});
执行效果如下图:
实现请求并发功能
声明一个存放等待状态的回调函数的变量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;
});
执行效果如下图:
总结
这道题的核心就是要解决缓存和并行执行两个问题,缓存就是将数据放在缓存池子中,按需存取即可,但是并行的话需要有回调函数存取的意识才能更好的实现。