浏览器分批发送大体积JSON数据时的异步执行问题求助
浏览器分批发送大体积JSON数据时的异步执行问题求助
我来帮你分析下这两种方法的问题,然后给出可行的解决方案~
先说说你两种写法的核心问题
第一种方法(for循环+await fetch)的问题
await setTimeout写法完全错误:setTimeout是基于回调的API,它不会返回Promise对象,所以你await它根本不会起到等待的作用——反而会因为await了一个非Promise值(定时器ID),代码会直接跳过这一步。- 可能存在变量名错误:你的数据源是
data,但在reduce里用的是array[i],这大概率是笔误,会导致chunk里的数据不对,甚至报错。 - fetch未处理HTTP响应错误:
fetch只有在网络层面出错时才会reject,对于4xx/5xx这类HTTP错误,它的Promise会resolve但response.ok是false。如果服务器没有正确响应,你的代码可能会看起来“卡住”,实际上是fetch的Promise虽然resolve了,但你没处理后续逻辑,加上之前的setTimeout错误,导致流程混乱。
第二种方法(Promise.all+map)的问题
Promise.all是并行执行所有请求:你用chunks.map(async chunk => ...)后接Promise.all,会同时启动所有chunk的异步任务——所有的setTimeout会同时开始计时,1秒后所有的fetch会同时发送,完全达不到“每批间隔1秒”的效果。而且同样存在await setTimeout的写法错误,根本起不到延迟作用。
正确的解决方案
第一步:封装sleep函数(把setTimeout转成Promise)
要实现真正的等待,必须把回调式的setTimeout包装成Promise:
function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
第二步:顺序分批发送(最直观的写法)
用for...of循环配合await,确保上一批发送完成+等待1秒后,再发送下一批:
const url = "http://localhost:3000/api/v1/test"; const chunkSize = 100; // 修正变量名错误:把array[i]改成data[i] const chunks = data.reduce((acc, _, i) => { if (i % chunkSize === 0) acc.push([]); acc[acc.length - 1].push(data[i]); return acc; }, [] as T[][]); // 顺序遍历每个chunk for (const chunk of chunks) { try { console.log('准备发送chunk,长度:', chunk.length); const response = await fetch(url, { method: 'POST', body: JSON.stringify(chunk), headers: { "Content-type": "application/json; charset=UTF-8" } }); // 必须处理HTTP错误,避免出现"卡住"的假象 if (!response.ok) { throw new Error(`HTTP请求失败,状态码:${response.status}`); } console.log('chunk发送成功,长度:', chunk.length); // 等待1秒后再发下一个 await sleep(1000); } catch (error) { console.error('发送chunk出错:', error); // 这里可以选择中断循环(加break),或者继续发送下一个,根据需求调整 } }
可选:用reduce构建顺序Promise链
如果你偏好链式写法,也可以用Array.reduce来构建顺序执行的异步链:
const url = "http://localhost:3000/api/v1/test"; const chunkSize = 100; const chunks = data.reduce((acc, _, i) => { if (i % chunkSize === 0) acc.push([]); acc[acc.length - 1].push(data[i]); return acc; }, [] as T[][]); // 用reduce串联所有异步任务 return chunks.reduce(async (prevTask, chunk) => { // 等待上一个chunk的发送+延迟完成 await prevTask; // 等待1秒 await sleep(1000); console.log('发送chunk,长度:', chunk.length); const response = await fetch(url, { method: 'POST', body: JSON.stringify(chunk), headers: { "Content-type": "application/json; charset=UTF-8" } }); if (!response.ok) { throw new Error(`HTTP请求失败,状态码:${response.status}`); } return response; }, Promise.resolve()); // 初始Promise,启动链式调用
关键要点总结
- 永远不要直接
await setTimeout,必须把它包装成Promise才能实现真正的等待。 - 要顺序执行带间隔的异步任务,
for...of + await是最直观、最容易维护的写法。 Promise.all是并行执行任务的API,完全不适合需要顺序间隔的场景。- 使用
fetch时一定要处理HTTP错误(检查response.ok),否则很容易出现“卡住”的假象。
内容来源于stack exchange
相关产品推荐
相关产品推荐

