大量计算或 DOM 更新怎样拆分?rAF、requestIdleCallback 能解决什么?
先理解原理和示例,再练习完整口述与追问。
回答要点
主线程长任务影响输入和绘制;rAF 在绘制前执行但不转移线程;idle 有预算且可能延迟;按时间片让出,CPU 计算可放 Worker。
原理拆解
浏览器中一段持续占用主线程的任务会推迟输入响应、样式布局和绘制,50ms 以上常被作为长任务观察。拆分时不要一次遍历几十万条并同步修改 DOM,而应控制每批工作量,以 performance.now 计算预算,保存进度后安排后续任务,让浏览器有机会处理输入和绘制。把所有分片连续放到 Promise 微任务中,可能仍不断清空微任务而阻塞渲染,不等于真正让出一帧。
requestAnimationFrame 适合与下一次绘制同步的轻量 DOM 更新,但回调仍在主线程,里面做 100ms 计算照样卡顿;刷新率也可能是 60、90、120Hz 等,不能每次固定按 16ms 推进。requestIdleCallback 适合可推迟的后台整理工作,查看 deadline.timeRemaining,并提供超时、取消与兼容回退,不能把必须及时完成的核心交互无限等待空闲。
纯 CPU 密集任务可以使用 Worker,把计算和消息传输成本一起测量。大列表还需要虚拟化减少 DOM 数量,批量写入避免交错读取布局,页面卸载或用户发起新任务时应取消旧分片。验收看输入延迟、长任务数量和总完成时间,拆分可能延长总时长,但应改善交互响应。
示例与推演
把 10 万条处理按每批约几毫秒预算拆分,保存游标,下一任务继续,期间允许输入与绘制。若只写 while 循环里 await Promise.resolve(),可能连续排入微任务,仍不给渲染机会。纯解析可交给 Worker,DOM 操作仍回主线程少量批量更新。
易错点
rAF 回调里做长计算仍会掉帧;requestIdleCallback 也不保证关键任务及时完成。取消与进度展示应与分片算法一起设计。
参考回答可直接练习口述
浏览器中一段持续占用主线程的任务会推迟输入响应、样式布局和绘制,50ms 以上常被作为长任务观察。拆分时不要一次遍历几十万条并同步修改 DOM,而应控制每批工作量,以 performance.now 计算预算,保存进度后安排后续任务,让浏览器有机会处理输入和绘制。把所有分片连续放到 Promise 微任务中,可能仍不断清空微任务而阻塞渲染,不等于真正让出一帧。