
本文旨在解决前端应用中处理大量网络请求时遇到的api超时、403错误等问题。通过分析常见的错误尝试,文章重点介绍了如何利用bluebird.map进行并发控制,以及如何手动实现分批处理和延迟执行请求,从而有效管理请求负载,避免api限流,确保应用稳定性和用户体验。
在现代Web应用开发中,我们经常需要向后端API发送大量网络请求,例如批量数据更新、文件上传等场景。然而,当请求数量达到一定规模(如数百甚至上千个)时,直接使用Promise.all等方式并行发送所有请求,往往会导致以下问题:
以下是一个典型的、可能导致问题的Promise.all实现示例:
const retry = async (requests: API.CarFailedRequest[]) => {
setIsLoading(true);
const res = await Promise.all(
requests.map(async request => {
try {
await service.retryFailedRequest(request);
return { status: true, request };
} catch (e) {
return { status: false, request };
}
})
);
setIsLoading(false);
return res;
};这段代码的问题在于,requests.map内部的async request => {...}函数会立即执行,从而创建并启动所有的Promise。Promise.all随后等待所有这些已启动的Promise完成,但此时所有的网络请求已经同时发出,导致上述问题。
在尝试解决上述问题时,开发者可能会采取一些看似合理但实际上未能达到预期效果的策略。理解这些尝试为何失败,对于构建正确的解决方案至关重要。
一种常见的尝试是引入像 Bluebird 这样的第三方库,它提供了更强大的并发控制能力。然而,如果使用不当,效果可能不佳。
// 错误的 Bluebird.map 使用方式
const retry = async (requests: API.CarFailedRequest[]) => {
setIsLoading(true);
const promises = requests.map(async request => {
try {
await service.retryFailedRequest(request);
return true;
} catch (e) {
return false;
}
});
await BlueBirdPromise.map(
promises, // 注意:这里传入的是一个已启动的Promise数组
async promise => {
try {
await promise;
} catch (err) {
console.log(err);
}
},
{ concurrency: 10 }
);
setIsLoading(false);
};这段代码的问题在于,requests.map 仍然在 BlueBirdPromise.map 调用之前就创建并启动了所有的 Promise。promises 数组中存储的是已经开始执行的网络请求。BlueBirdPromise.map 的 concurrency 选项虽然限制了同时处理 Promise 结果的数量,但它无法阻止这些 Promise 在被传入 map 之前就全部启动。因此,网络请求仍然是瞬间全部发出的。
另一种思路是手动将请求分成小块(chunks),并尝试在每个块之间添加延迟。
// 错误的 manual chunking 方式
const processPromisesWithDelay = async (promises: any[], delay: number, split: number) => {
const chunks = [];
for (let i = 0; i < promises.length; i += split) {
chunks.push(promises.slice(i, i + split));
}
for (const chunk of chunks) {
await Promise.all(chunk.map((promise: () => any) => promise())); // 问题在这里:promise() 意味着立即执行
await new Promise((resolve) => setTimeout(resolve, delay * 1000));
}
};
const retry = async (requests: API.CarFailedRequest[]) => {
setIsLoading(true);
const promises = requests.map(async request => {
await service.retryFailedRequest(request); // 再次:所有请求在这里就启动了
});
await processPromisesWithDelay(promises, 5, 5); // 传入的是已启动的Promise数组
setIsLoading(false);
};与 Bluebird.map 的误用类似,requests.map 在 processPromisesWithDelay 调用之前就启动了所有请求。即使 processPromisesWithDelay 试图分批处理并添加延迟,它操作的仍然是已经发出的网络请求。在浏览器网络面板中,你会看到所有请求几乎同时处于“pending”状态,只是它们的完成时间被分批等待了,而不是请求的启动时间被分批了。
解决上述问题的关键在于,并发控制应该作用于请求的启动时机,而不是 Promise 的解决时机。Bluebird.map 正是为此设计的,但需要正确使用它。
语流软著宝
AI智能软件著作权申请材料自动生成平台
228
查看详情
核心思想是:将原始数据(而不是已启动的 Promise)传递给 Bluebird.map,并在 map 的迭代器函数中按需启动每个网络请求。这样,concurrency 选项才能真正限制同时进行的请求数量。
import BlueBirdPromise from 'bluebird'; // 确保已安装 bluebird
const retry = async (requests: API.CarFailedRequest[]) => {
setIsLoading(true);
await BlueBirdPromise.map(
requests, // 直接传入原始的请求数据数组
async request => { // 在这里,当 Bluebird.map 允许时,才启动请求
try {
await service.retryFailedRequest(request);
// 可以根据需要返回状态或数据
return { status: true, request };
} catch (err) {
console.error("请求失败:", request, err);
// 返回失败状态,或者根据错误类型进行重试
return { status: false, request, error: err };
}
},
{ concurrency: 10 } // 同时只允许 10 个请求处于活跃状态
);
setIsLoading(false);
// 返回处理结果,Bluebird.map 默认会返回一个包含所有迭代器返回值的数组
// 例如:const results = await BlueBirdPromise.map(...)
// return results;
};代码解析:
这种方法确保了网络请求是分批、有控制地发出的,从而有效避免了API限流和超时问题。
如果不想引入 Bluebird 库,或者需要对分批和延迟有更精细的控制,可以手动实现一个分批处理函数。关键在于,我们需要传递的是返回 Promise 的函数,而不是已经启动的 Promise。
/**
* 按批次处理异步任务,并在批次之间添加延迟。
* @param taskFns 数组,每个元素是一个返回 Promise 的函数。
* @param chunkSize 每批处理的任务数量。
* @param delayMs 每批次之间的延迟时间(毫秒)。
* @returns 所有任务完成后的结果数组。
*/
const processTasksInChunksWithDelay = async <T>(
taskFns: (() => Promise<T>)[],
chunkSize: number,
delayMs: number
): Promise<T[]> => {
const results: T[] = [];
for (let i = 0; i < taskFns.length; i += chunkSize) {
const chunkFns = taskFns.slice(i, i + chunkSize);
// 在这里才调用函数,启动 Promise
const chunkPromises = chunkFns.map(fn => fn());
const chunkResults = await Promise.all(chunkPromises);
results.push(...chunkResults);
// 如果还有后续批次,则进行延迟
if (i + chunkSize < taskFns.length) {
await new Promise(resolve => setTimeout(resolve, delayMs));
}
}
return results;
};
const retryWithManualChunks = async (requests: API.CarFailedRequest[]) => {
setIsLoading(true);
// 将每个请求封装成一个返回 Promise 的函数
const requestTaskFns = requests.map(request => async () => {
try {
await service.retryFailedRequest(request);
return { status: true, request };
} catch (e) {
console.error("请求失败:", request, e);
return { status: false, request, error: e };
}
});
// 调用分批处理函数,每5个请求一批,每批之间延迟5秒
const allResults = await processTasksInChunksWithDelay(requestTaskFns, 5, 5000);
setIsLoading(false);
return allResults;
};代码解析:
这种手动实现方式提供了极大的灵活性,可以精确控制每批次的请求数量和批次间的延迟时间,适用于需要严格遵守API速率限制的场景。
处理大量网络请求是前端开发中的常见挑战。通过本文的探讨,我们了解到直接使用 Promise.all 可能会导致API超时和资源耗尽。关键在于控制请求的启动时机。无论是利用 Bluebird.map 的 concurrency 选项,还是手动实现分批处理和延迟,核心原则都是将大量请求分解为可管理的批次,并控制每个批次内的并发数以及批次间的间隔。通过采纳这些策略并结合最佳实践,开发者可以构建出更健壮、更高效、用户体验更好的应用。
以上就是优化大量网络请求:分批处理、并发控制与超时策略的详细内容,更多请关注其它相关文章!
# 关键在于
# 阳春seo推广费用
# 天津常规网络营销推广
# 宿迁seo公司认准15火星
# 奥克斯地产营销推广
# QQ社群营销推广方案
# 网站建设推广管理部门
# 谷歌搜索邯郸营销推广
# 成都网站建设开户
# 定西机场建设招标网站
# 闽侯网站seo哪个好
# 这段
# 并在
# 长时间
# 前端
# 而不是
# 重试
# 是一个
# 的是
# 在这里
# 前端应用
# 异步任务
# 应用开发
# ai
# 前端开发
# 后端
# 浏览器
相关栏目:
【
Google疑问12 】
【
Facebook疑问10 】
【
优化推广96088 】
【
技术知识133117 】
【
IDC资讯59369 】
【
网络运营7196 】
【
IT资讯61894 】
相关推荐:
Scipy Sparse CSR 矩阵非零元素行级遍历的最佳实践
《优志愿》修改手机号方法
咸鱼怎么设置仅粉丝可见的动态_咸鱼动态粉丝可见设置方法
天堂漫画网页版在线阅读 天堂漫画手机版入口
搜狗浏览器如何查找页面中的文字 搜狗浏览器Ctrl+F页面搜索功能
外媒评《燕云十六声》DIY载具新玩法:很像《塞尔达传说王国之泪》!
如何在CSS中实现盒模型多列间距_grid-gap与padding结合
《浙里办》电子发票开具方法
雨课堂官网在线登录 网页版雨课堂登录链接
抖音猜你想搜能说明对方搜过吗
什么是Satis,如何用它搭建一个私有的composer仓库?
《花瓣》创建专辑方法
优化Asyncio嵌套函数调度:使用生产者-消费者模式实现并发流处理
键盘保修需要什么_键盘售后维修流程
XPath动态元素定位:如何精准选择文本内容变化的元素
QQ网页版官方账号登录入口 QQ网页版网页版入口快速导航
LocoySpider如何批量采集电商商品_LocoySpider电商采集的模板应用
《华夏千秋》龙女试炼功法获取方法
AffinityDesigner图层蒙版怎么用_AffinityDesigner图层蒙版设计应用
sublime如何处理超大文件不卡顿 _sublime打开大日志文件技巧
第五人格PC版怎么避免被封号_第五人格PC版防封号注意事项
圆通快递官网入口查询单号 手机版官方查询入口
Mac hosts文件在哪里_Mac修改hosts文件详细教程
向往的生活小游戏启动处_向往的生活小游戏立即启动
键盘声音异常怎么回事_键盘异响怎么处理
Bootstrap 5导航栏折叠功能失效:数据属性迁移指南
CSS绝对定位与溢出控制:实现背景元素局部显示不触发滚动条
餐馆菜篮选购指南
excel怎么计算平均值 excel平均函数*ERAGE使用教学
iPhone 14 Pro如何更改区域设置_iPhone 14 Pro地区语言修改教程
Linux如何开发轻量级数据服务模块_Linux服务化设计
Magento 2 产品保存事件中安全更新属性的最佳实践
店铺如何做视频号推广?做视频号推广有用吗?
4399小游戏下装链接 4399小游戏下载链接入口
圆通快递包裹轨迹查询 圆通速递快件实时位置跟踪
《红果免费短剧》下载观看方法
教资成绩怎么查询
《全民k歌》音乐怎么下载到本地2025
小红书网页版在线直达 小红书网页版免费登录入口
智慧团建活动报名入口 智慧团建活动报名入口手机端官网
高德地图怎么查看未来行程规划_高德地图未来行程规划查看方法
POKI小游戏在线免费入口链接 POKI小游戏无下载秒玩玩
批改网官网首页登录 批改网学生用户登录入口
火狐浏览器如何刷新修复浏览器 火狐浏览器“重置Firefox”功能详解
PHP odbc_fetch_array 返回值处理:如何正确访问嵌套数组元素
Flexbox布局实践:实现底部页脚与顶部粘性导航条的完美结合
Lar*el Socialite单设备登录策略:实现用户唯一会话管理
PDF如何批量加注释_PDF多文件批注高亮操作教程
J*aScript类型数组_TypedArray使用
荣耀盒子应用管理技巧
2025-10-31
运城市盐湖区信雨科技有限公司是一家深耕海外推广领域十年的专业服务商,作为谷歌推广与Facebook广告全球合作伙伴,聚焦外贸企业出海痛点,以数字化营销为核心,提供一站式海外营销解决方案。公司凭借十年行业沉淀与平台官方资源加持,打破传统外贸获客壁垒,助力企业高效开拓全球市场,成为中小企业出海的可靠合作伙伴。