blog

同时调用后端数据接口但需要避免同时的重复请求,以缓解服务器压力

9 天前UTC+8

在 ZSFT 实现热度值时,遇到了列出字体列表时需要同时调用热度值 fetch 函数导致重复的多次请求,即便 fetch 的函数已经设置了如果本地有数据则返回本地数据,但同时调用的时间点本地确实没有数据,也就导致了这一问题。(这样的问题或许很常见?)在网络上也无法找到相关的建议 ... 当然或许是我的搜索关键词有问题 ...

ZSFT 的方法是返回全站的热度值数据并在过期前使用本地缓存数据,因为那的确并不大,并且可以大幅度降低服务器压力,但如果为每个数据碎片化的单个请求,则不适用此方法。

以下请直接查看源代码,包括过期处理:

// 正在请求标识
let getThisFetching = false;
// 返回的 Promise
let getFetchPromise = null;

async function get() {
    let obj = null;
    const localData = localStorage.getItem("localData");
    const now = new Date();
    const localDataTime = localStorage.getItem("localDataTime") ? new Date(localStorage.getItem("localDataTime")) : null;
    // 指定 1 天后过期,重新获取
    const oneDay = (new Date(now.getTime() + (1 * 24 * 60 * 60 * 1000))).toISOString();

    // 如果没有本地数据或者本地数据过期则执行,否则直接返回本地数据
    if (!localData || (localDataTime && localDataTime < now)) {
        // 如果正在请求为 true 则不再请求而直接等待返回
        if (!getThisFetching) {
            getThisFetching = true;
            getFetchPromise = fetch("XXX", {
                method: "GET",
            }).then(i => i.json()).then(data => {
                localStorage.removeItem("localDataTime");
                localStorage.setItem("localDataTime", oneDay);
                localStorage.setItem("localData", JSON.stringify(data));
                getThisFetching = false;
                getFetchPromise = null;
                return data;
            }).catch(error => {
                getThisFetching = false;
                getFetchPromise = null;
                throw error;
            });
        };
        obj = await getFetchPromise;
    } else {
        obj = JSON.parse(localData);
    };

    return obj;
};

实际上,这段代码和正常的可返回本地数据的 fetch 请求一样,只不过增加了两个变量, getThisFetchinggetFetchPromise ,它们位于函数外部,以防止每次调用时都被重置。

首先,判断 !getThisFetching 也就是是否正在被请求,如果是,则不执行请求,包括请求前,会将 getThisFetching 设置为 true ,代表正在请求, fetch 被赋值给 getFetchPromise ,这样就可以让 obj 等待它 obj = await getFetchPromise;

核心原理: fetch 被赋值在函数外部,每次调用则不会被重置状态,第一个请求会触发 fetch ,因为 !getThisFetching 成立,执行 fetch 然后等待 obj ,但到了第二个请求则不成立,就会直接等待 obj 也就是最终输出。