任务调度:有了setTimeOut,为什么还要使用rAF?

新的任务都是被放进消息队列中去的,然后主线程再依次从消息队列中取出这些任务来顺序执行。这就是我们之前介绍的消息队列和事件循环系统。

单消息队列的队头阻塞问题

再基于单消息队列的架构下,如果用户发出了一个点击事件或是缩放页面的事件,而在此时,该任务前面可能还有很多不太重要的任务在排队等待着被执行,诸如V8 的垃圾回收、DOM 定时器等任务,如果执行这些任务需要花费的时间过久的话,那么就会让用户产生卡顿的感觉。你可以参看下图:

在单消息队列架构下,存在着低优先级任务会阻塞高优先级任务的情况

Chromium 是如何解决队头阻塞问题的?

1. 第一次迭代:引入一个高优先级队列

观察上图,我们使用了一个优先级高的消息队列和一个优先级低消息队列
更进一步,将任务划分为多个不同的优先级,来实现更加细粒度的任务调度,比如可以划分为高优先级,普通优先级和低优先级,最终效果如下图所示:

2. 第二次迭代:根据消息类型来实现消息队列

虽然在交互阶段,采用上述这种静态优先级的策略没有什么太大问题的,但是在⻚面加载阶段,如果依然要优先执行用户输入事件和合成事件,那么⻚面的解析速度将会被拖慢。Chromium 团队曾测试过这种情况,使用静态优先级策略,网⻚的加载速度会被拖慢14%。
3. 第三次迭代:动态调度策略

这张图展示了 Chromium 在不同的场景下,是如何调整消息队列优先级的。通过这种动态调度策略,就可以满足不同场景的核心诉求了,同时这也是 Chromium 当前所采用的任务调度策略。
加载阶段-用户交互阶段-空闲阶段
4. 第四次迭代:任务饿死
好了,以上方案看上去似乎非常完美了,不过依然存在一个问题,那就是在某个状态下,一直有新的高优先级的任务加入到队列中,这样就会导致其他低优先级的任务得不到执行,这称为任务饿死。
Chromium 为了解决任务饿死的问题,给每个队列设置了执行权重,也就是如果连续执行了一定个数的高优先级的任务,那么中间会执行一次低优先级的任务,这样就缓解了任务饿死的情况。

此文章为5月Day4学习笔记,内容来源于极客时间《浏览器原理》,学习使我快乐,每天进步一点点💪💪

豪猪
4 声望4 粉丝

undefined