如何在React组件中分批或依次发起API请求以减轻后端压力?
解决大量API请求并发压垮后端的问题
你当前的问题是1000个项目×6种类型导致瞬间发起6000次请求,远超后端处理能力,以下是三种可行的解决方案:
方案一:请求队列控制并发数(推荐)
核心思路:不再让每个ObjectCount组件独立发起请求,而是在父组件中统一收集所有请求任务,用队列限制同时发起的请求数量(比如100个),请求完成后将数据分发给对应组件。
代码修改示例
父组件(统一管理请求)
class ProjectTable extends React.Component { constructor(props) { super(props); this.state = { projects: [], countData: {}, // 存储请求结果,key为`projectId-type`格式 concurrencyLimit: 100, // 并发请求上限 }; } componentDidMount() { // 假设已获取到projects数据,这里触发批量请求 this.fetchAllCounts(); } fetchAllCounts = async () => { const { projects, concurrencyLimit } = this.state; const taskList = []; const targetTypes = [1, 2, 3, 4, 6]; // 整理所有请求任务 projects.forEach(project => { targetTypes.forEach(type => { const taskKey = `${project.value}-${type}`; const requestUrl = `/api/foo/?type=${type}&project_id=${project.value}&format=json`; taskList.push({ key: taskKey, url: requestUrl }); }); }); // 并发控制请求函数 const batchFetch = async (tasks, limit) => { const resultMap = {}; // 分批次处理任务 for (let i = 0; i < tasks.length; i += limit) { const currentBatch = tasks.slice(i, i + limit); // 发起当前批次的所有请求 const batchPromises = currentBatch.map(async task => { try { const response = await axios.get(Settings.Host + task.url, { headers: { "Authorization": `Token ${cookie.load('token', { path: '/' })}` } }); resultMap[task.key] = response.data.count; } catch (err) { if (err.response?.status === 401) { cookie.remove("token", { path: '/' }); cookie.remove("email", { path: '/' }); window.location.assign('/'); } resultMap[task.key] = 0; // 出错时默认显示0 } }); await Promise.all(batchPromises); // 实时更新状态,让UI逐步渲染已完成的请求 this.setState({ countData: { ...resultMap } }); } return resultMap; }; await batchFetch(taskList, concurrencyLimit); }; render() { const { projects, countData } = this.state; const targetTypes = [1, 2, 3, 4, 6]; return ( <tbody> {projects.map((project, idx) => ( <tr key={idx}> <th>{project.label}</th> {targetTypes.map(type => { const taskKey = `${project.value}-${type}`; const count = countData[taskKey]; return ( <td key={type}> {count !== undefined ? ( <span>{count}</span> ) : ( <span>{Settings.Loading}</span> )} </td> ); })} </tr> ))} </tbody> ); } }
简化ObjectCount组件
此时组件无需自己发起请求,直接接收数据即可:
const ObjectCount = ({ count, loading }) => { return loading ? ( <span>{Settings.Loading}</span> ) : ( <span>{count}</span> ); };
方案二:合并请求(最优,需后端配合)
如果能协调后端开发添加批量接口(比如/api/foo/batch),可以一次性传入所有project_id和type,后端批量计算后返回结果,直接将6000次请求压缩为1次,对前后端压力都最小。
代码示例(父组件发起批量请求)
fetchBatchCounts = async () => { const { projects } = this.state; const targetTypes = [1, 2, 3, 4, 6]; try { const response = await axios.post( `${Settings.Host}/api/foo/batch`, { project_ids: projects.map(p => p.value), types: targetTypes }, { headers: { "Authorization": `Token ${cookie.load('token', { path: '/' })}` } } ); // 整理后端返回结果为可用格式 const countData = {}; response.data.forEach(item => { const taskKey = `${item.project_id}-${item.type}`; countData[taskKey] = item.count; }); this.setState({ countData }); } catch (err) { if (err.response?.status === 401) { cookie.remove("token", { path: '/' }); cookie.remove("email", { path: '/' }); window.location.assign('/'); } } };
方案三:添加请求延迟(临时应急方案)
如果暂时无法修改架构,可以给每个请求添加延迟,分散请求时间,避免瞬间爆发大量请求。
代码修改示例(修改ObjectCount组件)
class ObjectCount extends React.Component{ constructor(props){ super(props); this.state = { loading: true, data: {results:[]}, } } componentDidMount(){ // 从props接收延迟时间,父组件统一计算 const requestDelay = this.props.delay || 0; setTimeout(() => { var Host = Settings.Host; if (this.props.data_url){ const url = this.props.data_url.includes("?") ? `${Host}${this.props.data_url}&format=json` : `${Host}${this.props.data_url}?format=json`; axios.get(url,{ headers: { "Authorization":`Token ${cookie.load('token',{ path: '/' })}` } }) .then(res =>{ this.setState({ data: res.data, loading: false }); }) .catch(res =>{ if (res.response.status === 401){ cookie.remove("token", { path: '/' }); cookie.remove("email", { path: '/' }); window.location.assign('/'); } }); } }, requestDelay); } render (){ return this.state.loading ? ( <span>{Settings.Loading}</span> ) : ( <span>{this.state.data.count}</span> ); } }
父组件传入延迟参数
<tbody> {this.state.projects.map((project,i)=>{ const baseDelay = i * 50; // 每个项目的基础延迟 return( <tr key={i}> <th>{project.label}</th> <td><ObjectCount data_url={`/api/foo/?type=1&project_id=${project.value}`} delay={baseDelay} /></td> <td><ObjectCount data_url={`/api/foo/?type=2&project_id=${project.value}`} delay={baseDelay + 10} /></td> <td><ObjectCount data_url={`/api/foo/?type=3&project_id=${project.value}`} delay={baseDelay + 20} /></td> <td><ObjectCount data_url={`/api/foo/?type=4&project_id=${project.value}`} delay={baseDelay + 30} /></td> <td><ObjectCount data_url={`/api/foo/?type=6&project_id=${project.value}`} delay={baseDelay + 40} /></td> </tr> ) })} </tbody>
这种方式简单但总请求时间较长(1000个项目约需50秒),仅适合临时应急。
内容的提问来源于stack exchange,提问作者Zulfat
相关产品推荐
相关产品推荐

