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 属性,用来判断当前的回调函数是否因超时而被执行,是的话,didTimeouttrue

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

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);