🔁 一、Event Loop(事件循环)
🌟 概念核心:
JS 是单线程的,但为了不阻塞主线程,它采用了 异步非阻塞的执行模型,即:Event Loop。
👇 工作流程简要版:
- Call Stack(调用栈):同步代码进来就执行,执行完出栈。
- Web APIs:定时器、DOM 事件、XHR 等异步操作会交给浏览器处理。
- Callback Queue(任务队列):
- 宏任务(Macro Task):如
setTimeout
、setInterval
、setImmediate
、I/O
、requestIdleCallback
- 微任务(Micro Task):如
Promise.then
、MutationObserver
- 宏任务(Macro Task):如
- Event Loop 会不断循环:
- 清空调用栈
- 执行所有微任务
- 执行一个宏任务
- 再次检查微任务
- 如此反复…
📌 一句话理解:Event Loop 是让异步代码按顺序执行的调度机制。
🧘♂️ 二、requestIdleCallback(callback)
📍 作用:
用于在浏览器空闲时执行一些不重要、可延迟的任务,比如预加载、统计分析、懒加载等。
✅ 特点:
- 会在主线程空闲时调用,不影响页面流畅度。
callback(deadline)
:deadline.timeRemaining()
表示当前剩余空闲时间,最大 50ms。- 可以设置
timeout
防止永远不执行。
🎯 适合做什么?
- 页面加载完后,处理缓存、预加载资源、打点统计。
- 渲染不相关的长任务(可以切片)。
requestIdleCallback((deadline) => {
while (deadline.timeRemaining() > 0 && tasks.length > 0) {
performTask(tasks.shift());
}
});
🧊 三、requestAnimationFrame(callback)
📍 作用:
让 JS 在下一帧渲染前执行,专用于视觉动画相关的逻辑。
✅ 特点:
- 每秒最多执行一次,跟屏幕刷新率(一般是 60Hz)同步。
- 优化动画:在浏览器准备绘制之前运行 callback,不会掉帧。
- 会在下次“重绘”前被调用,适合更新 UI 的操作。
🎯 适合做什么?
- 平滑动画(移动元素、canvas 绘图)
- 渲染状态变化(比如滚动监听、进度条)
function update() {
drawSomething();
requestAnimationFrame(update); // 类似递归动画循环
}
requestAnimationFrame(update);
🧠 总结对比:
API | 触发时机 | 适合场景 | 优先级 |
---|---|---|---|
Event Loop | 控制任务调度 | 所有异步执行逻辑 | 高(根本) |
requestAnimationFrame | 下一帧绘制前 | UI 动画渲染 | 高(与屏幕刷新率同步) |
requestIdleCallback | 空闲时 | 后台逻辑、低优任务 | 低(不稳定) |
💡 实战经验:
- 动画用
requestAnimationFrame
,不要用setTimeout(fn, 16)
! - 后台任务别卡主线程,适合用
requestIdleCallback
处理碎片化逻辑。 - 长任务要考虑切片执行 + 微任务调度防止掉帧。