闭包实现批量合并请求工具函数,通过缓存请求队列、防抖延迟执行、统一发送并按序分发响应结果,支持自定义key、并发控制与失败隔离,避免全局污染。

用闭包实现批量合并请求的工具函数,核心是缓存待发送的请求、延迟执行、统一处理响应并按原顺序返回结果。闭包用来维持请求队列、定时器引用、以及每个请求的回调和参数等私有状态,避免全局污染,同时支持并发控制和去重(可选)。
基础版:合并同类型请求(如相同 URL + 方法)
适用于多个地方调用同一接口(如 getUser(1)、getUser(2)),希望合并成一次 GET /users?id=1,2 请求:
function createBatchFetcher(fetcher) {
// 闭包变量:缓存待合并的请求
const pending = new Map(); // key: 请求标识(如 url+method),value: { argsList, resolveList, rejectList }
let timer = null;
return function batchFetch(...args) {
// 生成唯一请求 key(可根据实际需求调整,比如 JSON.stringify({ url, method, params }))
const key = typeof args[0] === 'string' ? args[0] : JSON.stringify(args[0]);
// 初始化该 key 对应的请求批次
if (!pending.has(key)) {
pending.set(key, { argsList: [], resolveList: [], rejectList: [] });
}
const batch = pending.get(key);
// 存储当前调用的 Promise 控制器
const p = new Promise((resolve, reject) => {
batch.resolveList.push(resolve);
batch.rejectList.push(reject);
});
// 缓存本次请求参数(如 [id1], [id2])
batch.argsList.push(args);
// 防抖:等待 10ms,尽可能收集更多同 key 请求
if (timer === null) {
timer = setTimeout(() => {
timer = null;
pending.forEach((batch, key) => {
// 合并参数:例如把 [[1], [2], [3]] → { ids: [1,2,3] }
const mergedArgs = mergeArgs(batch.argsList);
// 执行真实请求(fetcher 应返回 Promise<Array>,每个元素对应一个原始请求的结果)
fetcher(key, mergedArgs)
.then(results => {
// 按顺序分发结果给各 resolve
batch.resolveList.forEach((resolve, i) => {
resolve(results[i]);
});
})
.catch(err => {
batch.rejectList.forEach(reject => reject(err));
})
.finally(() => {
pending.delete(key);
});
});
}, 10);
}
return p;
};
}
// 示例:简单合并 id 列表
function mergeArgs(argsList) {
return { ids: argsList.map(args => args[0]) }; // 假设每个 args 是 [id]
}
增强版:支持自定义 key + 并发限制 + 失败隔离
更实用的版本需支持不同请求不互相阻塞、单批最大请求数、失败不影响其他请求:
- 用
WeakMap或独立对象管理每组请求,key 由用户传入(如batchFetch('user', userId)) - 加
maxBatchSize限制单次合并数量,超量则切片或立即触发 - 每个子请求失败时只 reject 对应 Promise,不中断整批
- 使用
AbortController可选取消未发出的批次
使用示例:合并用户查询
假设后端支持 POST /api/users/batch 接收 ID 数组:
立即学习“Java免费学习笔记(深入)”;
const batchGetUser = createBatchFetcher(async (key, { ids }) => {
const res = await fetch('/api/users/batch', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ ids })
});
return res.json(); // 返回 [{id:1,...}, {id:2,...}]
});
// 这三行几乎同时调用,只会发出一次请求
const p1 = batchGetUser(1);
const p2 = batchGetUser(2);
const p3 = batchGetUser(3);
Promise.all([p1, p2, p3]).then(([u1, u2, u3]) => {
console.log(u1.name, u2.name, u3.name);
});
关键设计点说明
- 闭包封装了 pending 队列和 timer:外部无法直接修改,保证状态隔离
- Promise 构造器延迟绑定 resolve/reject:确保即使请求已发出,后续调用仍能拿到结果
- key 的设计决定合并粒度:可以是 URL、URL+method、甚至带 query 参数的完整签名
- 防抖时间不宜过长:通常 5–20ms,兼顾合并率与响应延迟
-
错误处理要细化:推荐后端返回部分成功结构(如
{ data: [...], errors: { '2': 'not found' } }),前端再映射回各 Promise


















