🟢 What —— 它是什么?
requestIdleCallback(callback[, options])
是浏览器提供的一个 API,用来在主线程空闲时执行一些优先级不高的任务。
它的特点:
- 异步执行:不会打断关键的渲染、交互、动画。
- 节省性能:只在浏览器判断“有空”的时候执行。
- 可控时限:通过
options.timeout
可以设置最迟必须执行的时间。
执行时,浏览器会传递一个 IdleDeadline 对象 给回调:
timeRemaining()
:返回当前帧剩余的空闲时间(毫秒)。didTimeout
:是否因为超时而强制执行。
🟢 Why —— 为什么需要它?
在 Web 应用里,任务往往有两类:
-
高优先级任务
- UI 渲染、用户交互、动画。
- 必须马上执行,否则就会“卡顿”。
-
低优先级任务
- 数据预加载、日志上报、分析埋点、缓存清理。
- 不影响用户体验,可以“等一等”。
👉 传统做法(如 setTimeout
)不够聪明,因为它只看时间,不知道浏览器忙不忙。
而 requestIdleCallback
能感知浏览器是否有空闲时间,在不阻塞渲染的情况下,把这些低优先级任务“插队”执行。
好处:
- 提升流畅度:避免任务和渲染抢 CPU。
- 提升效率:利用“碎片时间”做事,不浪费。
- 适合渐进加载:比如逐步渲染一大段数据。
🟢 How —— 怎么用?
基本用法
function heavyTask(deadline) {while (deadline.timeRemaining() > 0 && tasks.length > 0) {const task = tasks.shift();process(task);}// 任务没做完,继续安排if (tasks.length > 0) {requestIdleCallback(heavyTask);}
}requestIdleCallback(heavyTask);
解释:
- 每一帧有空闲时间就干活。
- 干不完就递归丢到下一个 idle。
- 避免一次性干太多,阻塞 UI。
设置超时
有时候,任务必须在某个时间点前完成:
requestIdleCallback((deadline) => {if (deadline.timeRemaining() > 0 || deadline.didTimeout) {sendAnalyticsData();}
}, { timeout: 2000 });
👉 即使 2 秒内浏览器都很忙,也会强制执行。
实际应用场景
-
日志 & 埋点
requestIdleCallback(() => {sendLogBuffer(); });
-
渐进渲染列表(虚拟列表之前的简单方式)
let i = 0; function renderChunk(deadline) {while (deadline.timeRemaining() > 0 && i < bigList.length) {renderItem(bigList[i++]);}if (i < bigList.length) requestIdleCallback(renderChunk); } requestIdleCallback(renderChunk);
-
预加载资源
requestIdleCallback(() => {new Image().src = "/next-page-banner.jpg"; });
⚠️ 注意事项
- 兼容性:Safari 不支持,需要
setTimeout
polyfill。 - 不适合实时任务:比如动画、UI 更新,不应该放进来。
- 有点“玄学”:空闲时间由浏览器决定,执行时机不可预测。