如何按来源将请求生成的Promise按10秒时间间隔批量处理?
按来源实现10秒窗口的Promise批量处理
这个需求本质是按请求来源做时间窗口内的请求聚合批量处理,我给你写一个简洁且健壮的实现方案,附带详细解释:
核心思路
我们需要给每个请求来源维护一个「等待批次」:
- 用
Map存储各来源的批次信息(定时器ID、待处理请求队列) - 收到请求时,先检查对应来源是否已有等待中的批次:
- 有则直接把请求加入队列
- 没有则创建新队列+启动10秒定时器,定时器到期后批量处理整个队列的请求
- 每个入队的请求会返回一个Promise,最终会在批次处理时拿到自己的执行结果
代码实现(JavaScript)
// 全局缓存:key=来源标识,value={定时器ID, 请求队列} const sourceBatchCache = new Map(); /** * 将请求加入对应来源的10秒批次队列 * @param {string} source - 请求的唯一来源标识(比如来源ID、域名等) * @param {Function} apiCallFn - 返回Promise的内部API调用函数(比如 () => fetch('/internal-api')) * @returns {Promise} 返回当前请求的最终执行结果 */ function batchRequestBySource(source, apiCallFn) { return new Promise((resolve, reject) => { // 检查当前来源是否已有正在等待的批次 if (sourceBatchCache.has(source)) { const batch = sourceBatchCache.get(source); // 将当前请求的回调和执行函数加入队列 batch.queue.push({ execute: apiCallFn, resolve, reject }); } else { // 初始化新批次的队列 const requestQueue = [{ execute: apiCallFn, resolve, reject }]; // 启动10秒定时器,到期后处理整个批次 const timerId = setTimeout(() => { // 先从缓存移除批次,避免后续请求重复触发 sourceBatchCache.delete(source); // 批量执行所有请求,用allSettled确保每个请求的结果都能被处理 Promise.allSettled(requestQueue.map(item => item.execute())) .then(results => { // 把每个请求的结果对应到各自的Promise回调 results.forEach((result, index) => { const target = requestQueue[index]; if (result.status === 'fulfilled') { target.resolve(result.value); } else { target.reject(result.reason); } }); }); }, 10000); // 10秒窗口时长 // 将新批次存入缓存 sourceBatchCache.set(source, { timerId, queue: requestQueue }); } }); }
关键细节说明
- 按来源隔离:用
Map确保不同来源的请求完全独立,不会互相干扰批次窗口 - 请求结果精准传递:每个入队请求的
resolve/reject会被单独存储,批量处理后一一对应返回,调用方感知不到批量逻辑,和直接调用API体验一致 - 健壮性保障:使用
Promise.allSettled而不是Promise.all,避免单个请求失败导致整个批次的所有请求都被标记为失败 - 内存泄漏预防:定时器触发后立即从缓存删除对应批次,同时可以补充一个清理函数来处理服务关停时的残留定时器:
// 清理所有未处理的批次定时器(比如服务 shutdown 时调用) function clearAllPendingBatches() { for (const { timerId } of sourceBatchCache.values()) { clearTimeout(timerId); } sourceBatchCache.clear(); }
使用示例
// 模拟来自不同来源的请求 batchRequestBySource('source-A', () => fetch('/internal-api?data=A1')) .then(res => console.log('A1结果:', res)) .catch(err => console.error('A1失败:', err)); // 10秒内来自source-A的第二个请求,会被加入同一个批次 setTimeout(() => { batchRequestBySource('source-A', () => fetch('/internal-api?data=A2')) .then(res => console.log('A2结果:', res)) .catch(err => console.error('A2失败:', err)); }, 5000); // 来自source-B的请求,会开启独立的批次 batchRequestBySource('source-B', () => fetch('/internal-api?data=B1')) .then(res => console.log('B1结果:', res)) .catch(err => console.error('B1失败:', err));
内容的提问来源于stack exchange,提问作者Learner
相关产品推荐
相关产品推荐

