如何延迟JS Promise实现JSON串行发送 避免并发500报错
问题根因
fetch 是异步非阻塞API,当前循环逻辑在攒够单批次15000行后,发起fetch请求不会等待请求响应完成,就会直接进入下一轮循环攒下一批数据、发新的请求,最终所有批次请求几乎在同一时间并发打到PHP后端,后端并发承载不足时就会出现批次截断、500错误。
另外原代码还有两个隐性问题:
resp.text是方法不是属性,需要加()调用才能拿到后端返回的文本内容- 读取完Excel就立刻弹出
Ready提示,此时实际数据还没开始上传,提示时机错误
实现串行批次发送的修改方案
核心用async/await语法改造异步逻辑,强制等待上一批次请求拿到后端成功响应后,再启动下一批次的发送,具体改动点:
- 给
ProcessjsonBud函数加async关键字,允许内部使用await等待异步操作 - 发起fetch的位置加
await,阻塞当前逻辑直到请求完成 - 增加错误中断逻辑,某一批次请求失败时直接终止后续批次发送,避免脏数据
- 修正提示时机,等所有批次全部上传完成后再弹出成功提示
- 修正原代码中
resp.text()的调用错误(原代码漏写括号,拿不到后端返回内容)
修改后可直接运行的代码
首先是Excel读取上传部分的代码(修正了提示时机):
let selectedFile; document.getElementById('importBUDid').addEventListener('change', (event)=>{ selectedFile = event.target.files[0]; }); document.getElementById('uploadBud').addEventListener('click', async ()=>{ let jsonObj; if (selectedFile) { let fileReader = new FileReader(); fileReader.readAsBinaryString(selectedFile); fileReader.onload = async (event)=>{ let dato = event.target.result; let workbook = XLSX.read(dato, {type:"binary"}); workbook.SheetNames.forEach(sheet => { jsonObj = XLSX.utils.sheet_to_row_object_array(workbook.Sheets[sheet]); }); // 等待所有批次上传完成后再弹提示 await ProcessjsonBud(jsonObj); alert('所有数据上传完成'); } } });
然后是改造后的批次发送逻辑:
async function ProcessjsonBud(dt1){ let d= new Date(); let TxtName = [d.getFullYear(), d.getMonth()+1, d.getDate()].join('_')+'_'+ [d.getHours(), d.getMinutes(), d.getSeconds()].join('_'); let Flagfiletxt = true; // 第一批次传true,通知PHP创建存储文件 const BATCH_SIZE = 15000; // 单批次行数,抽成常量方便后续调整 let jsonaux = []; const rowsavailables = dt1.length; for(let i = 0; i < rowsavailables; i++){ jsonaux.push(dt1[i]); // 攒够单批次大小,或者已经到最后一行,就发送当前批次 if (jsonaux.length === BATCH_SIZE || i === rowsavailables - 1){ console.log('当前发送批次行数:', jsonaux.length); let data = new FormData(); let jsonBlob = new Blob([JSON.stringify(jsonaux)], {type: "application/json"}); data.append("datajson", jsonBlob); data.append("Flagcreartxt", Flagfiletxt); data.append("TxtName", TxtName); try { // 等待当前批次请求完成,才会继续走后续逻辑 const resp = await fetch("model/importBud.php", { method: "POST", body: data }); if(!resp.ok) { throw new Error(`批次请求失败,状态码:${resp.status}`); } // 正确调用text()方法拿返回结果 const resText = await resp.text(); console.log('批次处理成功,返回内容:', resText); } catch (err) { console.error('上传出错,已终止后续批次发送:', err); alert(`上传失败:${err.message}`); // 出错直接抛错终止整个流程 throw err; } // 批次发送成功后再重置参数,准备下一批 Flagfiletxt = false; jsonaux = []; } } return true; }
额外优化建议
- 如果20万条数据拆分后批次较多,可以在前端加进度条提示,每完成一个批次更新一次进度,用户体验更好
- PHP端建议每处理完一个批次就显式返回成功响应,不要等到所有逻辑跑完再返回,避免请求超时
- 单批次大小可以根据PHP的
post_max_size、max_execution_time配置灵活调整,不一定固定15000行
内容的提问来源于stack exchange,提问作者madison_sg
相关产品推荐
相关产品推荐

