Skip to main content

利用浏览器的空闲时间

之前 render 函数存在的问题​

最初的 render 函数是通过递归实现的。

当处理一个非常大且深的虚拟 DOM 树时,单线程的 JavaScript 会不断递归地执行 render 操作,这最终可能导致浏览器后续渲染过程的阻塞。

如何使用 window.requestIdleCallback API​

通过浏览器提供的 requestIdleCallback 接口,可以利用浏览器空闲时间。

requestIdleCallback(callback, options) 函数可以插入一个函数,使其在浏览器空闲时间被调用。

参数 options 里只有执行超时时间 timeout 一个属性。

requestIdleCallback 能在主事件循环上执行后台和低优先级工作,而不会影响延迟关键事件,如动画和输入响应。

函数一般会按先进先调用的顺序执行。

然而,如果指定了执行超时时间 timeout,则有可能为了在超时前执行函数而打乱执行顺序。

具体而言,如果指定了 timeout,并且有一个正值,而回调在 timeout 毫秒过后还没有被调用,那么回调任务将放入事件循环中排队,即使这样做有可能对性能产生负面影响。

mini-react 里,直接按照先进先调用的顺序执行就可以。

IdleDeadline 是在调用 Window.requestIdleCallback(callback) 时创建的闲置回调 callback 的输入参数。

它提供了两个方法:

  • timeRemaining() 方法,用来判断预计还剩余多少闲置时间;
  • didTimeout 属性,用来判断当前的回调函数是否因超时而被执行,是的话,didTimeout 为 true。

知道浏览器的空闲时间有多久后,就能利用空闲时间执行特定任务。

requestIdleCallback 与宏任务和微任务的关系​

requestIdleCallback 计划的任务不属于宏任务或微任务的范畴,其在事件循环的空闲期执行。

这通常发生在当前帧的宏任务和微任务执行完毕后,且在浏览器认为有足够空闲时间可以用于处理低优先级工作时。

相比于宏任务和微任务,requestIdleCallback 计划的任务的优先级更低。

它们是在浏览器空闲时才执行,因此不会阻塞或延迟关键的用户界面更新和响应。

在实践中,requestIdleCallback 通常用于执行后台或低优先级的任务,如日志记录、数据分析、更新非关键的用户界面元素等。

在每个宏任务中都执行特定任务​

下列代码中,workLoop 函数只会在第一个宏任务(T1)结束后被执行。

requestIdleCallback(workLoop);

需要在 workLoop 中继续调用 requestIdleCallback(workLoop) ,

才能保证在每个宏任务结束后,浏览器都会去执行特定任务。

function workLoop() {
...

requestIdleCallback(workLoop)
}

完整代码:

function workLoop(idleDeadline) {
let shouldYield = false;
while (!shouldYield) {
// do something

shouldYield = idleDeadline.timeRemaining() < 1;
}

requestIdleCallback(workLoop);
}

requestIdleCallback(workLoop);