React中使用Promise.all批量发起Apollo查询耗时随请求数增长问题
问题成因
- 代码逻辑错误:你定义的
async function getTimeSeriesQuery(i)没有返回apollo.query的执行结果,导致你推入promises数组的都是立即完成的空Promise,Promise.all(promises)的完成时机根本不绑定实际请求的返回状态,你实际统计的是所有请求串行排队/返回的总耗时,而非并行请求的耗时。 - 浏览器同域名并发限制:HTTP/1.1协议下浏览器对同一个域名的并行请求数限制通常为6个,超过限制的请求会进入浏览器的待发送队列排队等待,若你的请求数量远大于6,总耗时会近似于
Math.ceil(请求总数/6)*单请求耗时,呈现线性增长特征。 - 客户端/服务端限制:Apollo Client如果配置了请求限流、批处理延迟规则,或者服务端对同一客户端的请求做了频率限制、串行处理逻辑,也会导致请求无法真正并行,总耗时线性增长。
- HTTP协议队头阻塞:如果接口未启用HTTP/2,HTTP/1.1的TCP队头阻塞问题也会加剧高并发请求下的耗时增长。
优化方案
- 修复基础代码错误:调整
getTimeSeriesQuery的逻辑,返回请求对应的Promise,确保Promise.all能正确等待所有请求完成,修正后的代码如下:
// 去掉无用的async标识,直接返回query的Promise function getTimeSeriesQuery(i) { return apollo.query(getChunkQueryOptions(i)) } const promises = [] // 把var改为let避免循环变量共享问题 for(let i = 0; i < numberChunks; i++) { promises.push(getTimeSeriesQuery(i)) } // 需要await或者加.then才能拿到实际返回数据 const data = await Promise.all(promises)
- 控制请求并发数:不要一次性发起所有请求,手动实现并发控制器或者引入工具库限制同时发送的请求数(通常设为5~6适配HTTP/1.1的并发限制),避免请求在浏览器队列排队产生额外耗时。
- 合并请求:如果服务端支持,开启Apollo Client的请求批处理能力,将多个分片的GQL查询合并为单个请求发送,直接将总耗时降低到接近单请求的水平。
- 优化协议和服务端配置:接口启用HTTP/2,解除同域名并发数限制;协调服务端调整限流策略,允许合理范围内的并行请求。
- 调整分片策略:适当调大单个请求的时序数据分片大小,降低总请求数,从根源上减少多请求带来的额外开销。
内容的提问来源于stack exchange,提问作者Mohamad Abdel Rida
相关产品推荐
相关产品推荐

