Fetch 卡顿源于 res.json() 同步解析大 JSON,应改用 Response.body 流式读取+分块解析或 Web Worker 离线解析。

Fetch 本身不直接处理 JSON 反序列化,卡顿发生在 .then(res => res.json()) 解析大 JSON 字符串时 —— 这是主线程同步阻塞操作。requestIdleCallback 不能“插入”到 fetch 流程中自动分片解析,但可以配合流式响应(Response.body)+ 分块读取 + 增量解析来规避主线程长时间冻结。
用 ReadableStream 分块读取响应体
避免一次性加载整个 JSON 字符串到内存,改用 response.body 获取流,逐段读取并拼接合法 JSON 片段(需确保服务端支持流式 JSON 或输出 NDJSON/JSON Lines):
- 服务端应按行输出 JSON 对象(如每行一个对象),或返回可分块的 JSON 数组(需自行维护括号平衡)
- 前端用
response.body.getReader()获取 reader,循环调用reader.read()获取Uint8Array数据块 - 将每个 chunk 转为字符串后,追加到缓冲区;仅当缓冲区形成完整 JSON 单元(如一行、一个对象)时才解析
在空闲时段解析已就绪的数据块
把解析逻辑放入 requestIdleCallback,避免抢占用户交互时机:
- 每次攒够一个可解析单元(例如一行 JSON),不立即
JSON.parse(),而是推入待处理队列 - 调用
requestIdleCallback(processQueue, { timeout: 2000 }),在空闲时批量解析队列中的片段 - 解析后触发回调或更新状态,同时检查是否还有剩余数据,有则继续调度
注意边界与容错
流式 JSON 解析比全量解析更易出错,需主动防御:
立即学习“Java免费学习笔记(深入)”;
- 缓冲区需处理跨 chunk 的 JSON 边界(如
{ "name": "a和lice" }分属两个 chunk) - 推荐使用成熟的流式 JSON 解析器(如 stream-json)处理嵌套结构,而非手写分割逻辑
- 设置超时和最大缓冲长度,防止内存无限增长;解析失败时跳过该块并记录错误
替代方案:Web Worker + Offscreen 解析
若数据结构复杂、无法流式拆分(如单一大对象),更稳妥的方式是把 JSON.parse() 移出主线程:
- 用
response.text()获取完整字符串后,通过postMessage发送给 Web Worker - Worker 内执行
JSON.parse(),完成后将结果发回主线程 - 主线程全程不阻塞,UI 响应不受影响;适合 GB 级 JSON 场景


















