根本原因是同步执行阻塞主线程超50ms,应按真实耗时(performance.now)动态切片至10–25ms/批,结合硬件信息初设批大小(20–200条),用requestIdleCallback调度并timeout兜底,纯计算任务移交Web Worker。

超大数据格式化(比如解析几万行 JSON、转换大型嵌套对象、生成结构化预览)卡顿,根本原因不是“数据太大”,而是同步执行时 JavaScript 主线程被独占超过 50ms,导致页面冻结、按钮无响应、滚动掉帧。拆分长任务不能靠“快”,而要让主线程有空隙处理渲染和交互——关键在调度时机和切片方式。
按执行时间而非条数切片,动态校准每批耗时
固定每批处理 100 条,在低端机上可能跑 40ms,在高端机上却只用 3ms,既不安全也不高效。真实做法是:
- 用 performance.now() 在每批前后打点,实测真实耗时
- 目标单批控制在 10–25ms,超过即自动减小批大小(如 ×0.7)
- 结合 navigator.hardwareConcurrency 和 deviceMemory 初设基准值(例如 4 核 + 4GB 内存 → 初始批大小设为 80)
- 始终设硬边界:最小不小于 20 条,最大不超过 200 条,防震荡
用 requestIdleCallback 做主调度,带 timeout 防止挂起
它专为低优先级后台任务设计,浏览器会在空闲时调用,天然避开输入、动画等高优时机。但要注意兼容性和兜底:
- 必须加 { timeout: 2000 },避免用户切到其他标签页导致任务永久等待
- 旧版 Safari 不支持?降级用 setTimeout(fn, 0),虽精度略低但保障可用
- 不要在循环里反复读取 offsetHeight 或 getBoundingClientRect(),这类操作会强制触发同步 layout,瞬间把空闲时间吃光
纯计算任务坚决剥离主线程,交给 Web Worker
如果格式化过程不含 DOM 操作(如 JSON.parse + 递归扁平化 + 类型校验),就别在主线程里折腾切片了:
- 把整个数据和逻辑移入 Web Worker,主线程只负责发送和接收消息
- Worker 内可放心同步遍历 10 万条,完全不影响 UI 响应
- 配合 Transferable Objects(如 ArrayBuffer)零拷贝传递大数组,避免序列化开销
配套轻量但致命的优化项
只拆任务不够,很多卡顿来自隐性陷阱:
- 禁用内联样式计算:不在格式化循环中访问任何触发 layout 的属性
- 批量构造结果:先拼成字符串或对象树,最后一次性返回,避免中间状态暴露
- 对用户可见的进度反馈,用 requestAnimationFrame 更新,不卡帧











