使用Promise.all并发解析时,为何每个Promise耗时线性增长?
深入解释
1. 这不是JavaScript单线程的固有特性
JavaScript的单线程指的是主线程同步代码的串行执行逻辑,但Promise包裹的异步操作(比如HTTP请求)是由浏览器/Node.js底层的IO线程池处理的,并不会阻塞主线程。所以耗时递增的现象,本质和单线程执行无关,核心原因来自以下几个层面:
2. 网络层的并发连接限制
- 客户端并发上限:浏览器和Node.js对同一域名的并发HTTP请求都有默认限制——浏览器一般是6-8个,Node.js通过
http.Agent默认是5个。当你一次性发起100个请求时,超过上限的请求会被放入等待队列,必须等前面的请求完成、释放连接后才能真正发起。这段排队等待的时间被计入了你的计时,导致后续Promise的耗时看起来越来越长。 - 服务器端限流策略:目标API服务器大概率会有并发请求限制,比如通过队列缓冲过量请求,或者对高频请求增加响应延迟,防止服务器过载。这种情况下,后续请求会因为服务器端的排队或限流而变慢。
3. 代码计时逻辑的误差
你代码里的计时存在隐性问题:new Promise(async (res) => { ... })是冗余写法(async函数本身就返回Promise),且你的计时从Promise创建时开始,但实际上,当Promise被加入列表时,异步函数就已经开始执行——但由于连接限制,后面的请求根本没真正发起HTTP请求,而是在等待连接释放,这段等待时间被算进了耗时统计,放大了"耗时递增"的现象。
4. 系统资源竞争
同时发起大量异步操作会引发系统资源竞争:
- 网络带宽被占满,后续请求的数据包传输延迟增加;
- Node.js的IO线程池被耗尽,后续IO操作需要等待线程空闲;
- 当大量Promise同时resolve时,主线程需要处理大量回调函数,短暂阻塞也会让后续计时结果变长。
验证与优化方向
- 验证连接限制:将请求分散到不同域名,或者修改axios的
httpAgent配置提高并发数,耗时递增的现象会明显缓解; - 修正计时逻辑:在
getSomeData内部发起请求前开始计时,排除排队等待时间,能准确统计实际请求耗时; - 控制并发量:使用
p-limit这类并发控制库,或者手动实现批量并发,避免一次性发起过多请求,既解决耗时问题,也能避免触发服务器限流。
内容的提问来源于stack exchange,提问作者Jas Singh
相关产品推荐
相关产品推荐

