页面卡死根源是主线程被长时间独占;应改用可中断的for循环分块处理,配合setTimeout或queueMicrotask让出控制权,复用数组、用Map存唯一键,并优先考虑服务端去重或Web Worker卸载压力。

处理超大数组(比如百万级)去重时,页面卡死的根源是主线程被长时间独占——JavaScript 是单线程的,一次性遍历、创建大量中间数组或频繁 GC 都会阻塞渲染和用户交互。关键不是“选哪个去重方法”,而是“怎么拆解任务、让出控制权、控制内存峰值”。
用 for 循环代替高阶函数,手动控制节奏
filter、map、reduce 等方法会一次性生成新数组并全程占用执行栈;而原生 for 循环可自由中断、分批处理、复用变量。
- 避免 arr.filter(...) 或 [...new Set(arr)] 这类“一行式”写法,它们内部仍需遍历全部元素并建新结构
- 改用普通 for,每处理 5 万~10 万个元素后,用 setTimeout(..., 0) 或 queueMicrotask 让出主线程,恢复 UI 响应
- 复用结果数组(如提前分配长度或用 push 而非 concat),减少内存抖动
用 Map 存键值,不存整个对象
对象数组去重最耗内存的不是循环,而是把整条数据反复存进临时结构里。只要业务有自然唯一字段(如 id、sku、userId),就只存这个键。
- 示例:const seen = new Map(); const unique = []; for (const item of bigArray) { if (!seen.has(item.id)) { seen.set(item.id, true); unique.push(item); } }
- 比 new Set(bigArray) 节省数倍内存:Map 只存轻量 key,Set 会尝试存储整个引用对象
- 若无单一字段,可用组合键:const key = `${item.a}|${item.b}`,但注意字符串拼接开销,10 万以上慎用
超过 20 万条就考虑分块 + 异步调度
浏览器对单次 JS 执行时长敏感(通常 >50ms 就会感知卡顿),所以不能只靠“优化算法”,必须做时间切片。
- 把大数组切成每块 2~5 万条,用递归 + setTimeout 处理每一块
- 配合状态标记(如 let offset = 0; function processChunk() { ... offset += chunkSize; }),支持暂停/恢复
- 需要实时反馈时,可在每块结束后更新进度条或提示“已处理 X 条”
极端情况:服务端或 Web Worker 卸载压力
如果数组来自接口且总量稳定在百万级以上,前端硬扛不是最优解。
- 优先推动后端加 DISTINCT 或用数据库去重(如 PostgreSQL 的 SELECT DISTINCT ON (id) *)
- 前端确实要处理时,用 Web Worker 把去重逻辑移出主线程,Worker 内仍可用 Map + for 分块,完成后 postMessage 返回结果
- 避免在 Worker 中传大对象数组——先序列化为 JSON 字符串再传,Worker 解析后处理,减少主线程内存拷贝


















