浏览器是单线程的,这意味着JavaScript执行、页面布局和绘制都在同一个主线程上完成。
任何耗时过长的JS任务都会阻塞后续的渲染工作,导致页面卡顿、掉帧,甚至完全失去响应。
setTimeout(fn, 0) 用于执行我们希望尽快运行但不紧急的任务,我们期望它能将任务推迟到未来的某个时间点,让主线程喘口气:
setTimeout(() => {
// 在未来的某个时间点执行这个函数
console.log("Hello, after 10000ms!");
}, 10000);
setTimeout的困境:不守时
不过,它很快暴露出一个致命缺陷:它不在乎浏览器正在做什么。
setTimeout
- 延迟不准确:它可能不会在预定时间点执行,当主线程繁忙时任务会被排队
- 时机不佳:在浏览器渲染时插队,导致掉帧和卡顿
requestAnimationFrame
为了解决动画卡顿问题,浏览器厂商意识到需要一个与”视觉”同步的调度API。于是,(rAF)应运而生。
requestAnimationFrame
function animate() {
// 在这里执行一些 DOM 样式操作,列如改变元素的位置、透明度等
// ...
// 再次调用 requestAnimationFrame,让动画持续进行
requestAnimationFrame(animate);
}
// 启动动画循环
requestAnimationFrame(animate);
rAF 的设计目标超级明确:在浏览器下一次重绘之前执行回调函数。
rAF的局限性
rAF 它完美解决了高优先级视觉任务的调度问题,但如果我们把一个不紧急、耗时的后台任务(列如发送日志、数据预处理)放进其中,就又回到了老问题:它依旧会阻塞关键渲染路径,导致卡顿。
requestIdleCallback
requestIdleCallback (rIC) 标志着浏览器调度进入了”智能化”和”协作化”时代。
rIC 的核心思想是在浏览器主线程的”空闲期”执行低优先级回调函数。
requestIdleCallback((deadline) => {
// This will only be executed when the browser is idle
console.log('浏览器闲暇时间可以做一些工作了');
});
rIC的智慧:见缝插针
- rIC 调度器会在每帧结束时检查剩余时间(此时布局、绘制等高质量任务已完成),只有当主线程空闲时才会执行rIC的回调
- 协作式调度与deadline:它的回调函数接收一个deadline对象,其中最重大的是timeRemaining()方法,用于返回可用的空闲时间
requestIdleCallback((deadline) => {
// 只有当浏览器空闲时才会执行这里的函数
console.log('Browser is idle, let's do some work!');
});
这种模式将可能阻塞线程的大任务分解为较小的任务块。在每个空闲期,只处理一小部分任务,剩余工作通过判断主线程是否应该”让步”,推迟到下一个空闲周期处理。
deadline.timeRemaining()
代码不是霸占线程,而是与浏览器友善协商,共同维护用户体验。
© 版权声明
文章版权归作者所有,未经允许请勿转载。如内容涉嫌侵权,请在本页底部进入<联系我们>进行举报投诉!
THE END















暂无评论内容