同一个请求发送多次怎么办?合并一下

场景

组件A和组件B,需要根据 id 发送请求获取用户信息并进行不同的处理,它们分别用于不同的模块,这些模块也可以在不同的页面中自由组合。进入页面,几乎同时发送多个相同的请求,入参可能相同也可能不一样。

虽然又不是不能用,但是怎么能忍呢,必须得优化!将它们合并成☝一个请求。

当然,前提是接口能够支持批量请求。

思考

既然组件AB属于兄弟关系,当然可以把批量请求抽取出来,由父组件负责调用并存储数据,这也能用,只是会和父组件耦合,带来一定的维护成本。

能不能让请求逻辑保持在组件A和组件B内部,将它们一定时间段内发出的请求,优雅的合并起来呢?

接口请求是异步的,利用好 Promise 自然能实现😎。

实现

需要具备的功能点有:

  • 缓存来自组件的请求
  • 批量请求参数超过上限时,支持分批请求
  • 缓存批量请求返回的数据
  • 请求回来后,通知对应的组件

问题:

  • 假请求(单独请求)并不是实际发送的请求,当真实请求(批量请求)的数据返回后,如何准确通知对应的单独请求?
  • 批量请求中可能有部分查询失败的结果,如何处理查不到数据的请求?

缓存来自组件的请求

先定义一个用来处理合并请求的类 MergeRequest,请求池和缓存需要全局唯一,因此暴露给外部的实际上是它的单例。

typescript 复制代码
class MergeRequest () {
    public cache = new Map(); // 缓存服务端的数据
    public requestMap = new Map(); // 请求池,缓存来自组件的请求,以及记录请求的状态
}
export const mergeRequest = new MergeRequest()

然后需要有提供给组件发起请求的方法。

javascript 复制代码
class MergeRequest () {
    ...
    fetchData(id) {
        return new Promise((resolve) => {
            this.addRequest(id, resolve);
        })
    }
    ...
}

当组件调用时添加请求任务到请求池中,等待发送。

kotlin 复制代码
class MergeRequest () {
    ...
    getInitRequest() {
        return {
            resovles: [], // 可能会有相同参数的请求,
            status: 'ready',
        }
    }
    
    addRequest(id, resolve) {
        const request = this.resolves.get(id) || this.getInitRequest();
        request.resolves.push(resolve); // 缓存 resolve,当批量请求结果返回时通知组件
        this.resolves.set(id, request);
        this.fetchDataList(); // 发送批量请求
    }
    ...
}

批量请求参数超过上限时,支持分批请求

由于需要支持设置一次请求参数的个数上限,因此每发送一次请求,只获取状态为ready的 id,并将状态修改为pending,避免请求之间参数重复。

kotlin 复制代码
class MergeRequest () {
    ...
    getIds() {
        const ids = [];
        for (const [key, val] of this.requestMap) {
            if (val.status === 'ready') {
                ids.push(key);
                this.requestMap.set(key, { ...val, status: 'pending' })
            }
        }
        return ids;
    }
    ...
}

将批量请求推入微任务队列等待执行。

scss 复制代码
class MergeRequest () {
    ...
    fetchDataList(id) {
        Promise.resolve().then(() => {
            let ids = getIds();
            while (ids.length) {
                this.fetchList(ids);
                ids = getIds();
            }
        )
    }
    ...
}

缓存批量请求返回的数据

当服务端返回数据后,缓存起来,其他组件请求时先查询缓存。

javascript 复制代码
class MergeRequest () {
    ...
    fetchDataList(id) {
        Promise.resolve().then(() => {
            let ids = getIds();
            while (ids.length) {
                this.fetchList(ids).then(list => {
                    this.cacheData(list); // 缓存结果
                });
                ids = getIds();
            }
        )
    }
    
    cacheData(list) {
        list.forEach(item => {
            this.cache.set('id', item)
        })
    }
    
    fetchData(id) {
        // 先查询缓存
        const data = this.cache.get(id)
        if (data) {
            return Promise.resolve(data);
        }
        return new Promise((resolve) => {
            this.addRequest(id, resolve)
        })
    }
    ...
}

请求回来后,通知对应的组件

同时需要通知等待中的组件,并将请求任务从缓存池中移除。

javascript 复制代码
class MergeRequest () {
    ...
    fetchDataList(id) {
        Promise.resolve().then(() => {
            let ids = getIds();
            while (ids.length) {
                this.fetchList(ids).then(list => {
                    this.cacheData(list); // 缓存结果
                    this.flushRequest(); // 通知等待中的组件
                });
                ids = getIds();
            }
        )
    }
    
    flushRequest(list) {
        list.forEach(item => {
            const target = this.requestMap.get(item.id);
            target.resolves.forEach(res => res(item)); // 将数据返回给组件
            this.requestMap.delete(item.id); // 删除请求任务
        })
    }
    ...
}
相关推荐
中文呀5 小时前
三维 GIS 离线缓存实战:逆向 CesiumOfflineCache,并亲手为它补上"自动更新"能力
前端
前进前进前进6 小时前
TRAE 全链路实战:做了一个警示牌一键生成器:纯 Canvas、单文件、零依赖
前端
小万7427 小时前
God‘s Eye View 深度解析:15 路公开信号 + CesiumJS 的浏览器级 OSINT 控制台,60 帧渲染的“真实间谍卫星模拟器”
前端
IT星宿7 小时前
浏览器标签页全是一个图标:我用名字哈希生成 favicon,让每个项目都有身份标识
前端
OpsEye7 小时前
大模型账单对账难:不同厂商 Token 统计口径差异怎么处理
javascript·ai编程
全栈项目管理程序猿7 小时前
ArcGIS JS 基础教程(32):体元系列 - VoxelSlice 体元切片
前端·javascript
拖孩10 小时前
AI 帮我写的小程序「厨菜记」上线满月了,挣了两百块!!!
前端·后端·微信小程序
用户3600555790010 小时前
Kestrel_VLLM - 13 · 为什么排除 MoE FFN Tiling 与 Tensor Core
前端
Hilaku10 小时前
AI 流式输出都用 SSE,什么时候必须换 WebSocket?
前端·javascript·程序员
奇牙coding10 小时前
Codex CLI 升级后多文件编辑静默失效:config.yaml 枚举变更排查与修复指南
java·前端·javascript·ai