react怎么做的优先级调度

1,150 阅读4分钟

前文

react的架构浅析

我们知道每次setState会触发重新渲染,就会启动上面链接提到的render+commit渲染流程,

我们的应用可能会同时调用很多次setState,那怎么去处理他们的调用顺序?这就是本文所探讨的问题

react模式

  • legacy 模式: ReactDOM.render(<App />, rootNode)。这是当前 React 18之前使用的方式。当前没有计划删除本模式,但是这个模式可能不支持这些新功能。
  • concurrent 模式: ReactDOM.createRoot(rootNode).render(<App />)。 React 18的默认开发模式。这个模式开启了所有的新功能。(并发模式)

同步流程

react17及之前默认使用的是legacy模式

多次的setState按顺序处理,分多次进行render+commit渲染流程,

存在的问题

如果input输入过程中,input关联的state的变化引起了其他state的变化,两个 setState 一起发生,如果这个渲染流程中处理的 fiber 节点比较多,渲染一次就比较慢,这时候用户输入的内容可能就不能及时的渲染出来,用户就会感觉卡,体验不好。可能会影响到input输入的体感,会产生卡顿的感觉(这个我就有真实遇到过,后来是把某个颗粒化的列表元素组件进行了useCallback+React.memo缓存,减少了每次渲染处理的fiber节点)

并发流程

react18默认使用的是concurrent模式,首先并发是多个任务轮流使用一个cpu,并行是有多个cpu共同处理多个任务

  • 同步模式是循环处理 fiber 节点,并发模式多了个 shouldYield 的判断,每 5ms 打断一次,也就是时间分片。并且之后会重新调度渲染。通过这种打断和恢复的方式实现了并发。
  • 然后 Scheduler 可以根据优先级来对任务排序,这样就可以实现高优先级的更新先执行。
  • 每次 setState 引起的渲染都是由 Scheduler 调度执行的,它维护了一个任务队列,上个任务执行完执行下个,被打断的任务会添加到Scheduler的任务队列里面
function workLoop() {
// 每处理一个fiber节点,都判断下是否打断
**// shouldYiled 方法就是判断待处理的任务队列有没有优先级更高的任务,有的话就先处理那边的 fiber,这边的先暂停一下。
//shouldYiled根据过期时间,每次开始处理时记录个时间,如果处理完这个 fiber 节点,时间超了,那就打断。这个就是时间分片
//优先级高低会影响 Scheduler 里的 taskQueue 的排序结果,但打断只会根据过期时间。**
  while (wip && shouldYield()) {
    performUnitOfWork();
  }

  if (!wip && wipRoot) {
    commitRoot();
  }
}
// workInProgress记录每个workLoop处理的fiber节点,然后根据workInProgress是否是null判断是否全部 fiber 节点都渲染完
// 根据wip 是不是 null判断是不是中断(中断的话wip不为null)

优先级调度

Schedular的优先级

Untitled.png

图片from:神光 juejin.cn/post/717123…

Immediate 是离散的一些事件,比如 click、keydown、input 这种。

UserBlocking 是连续的一些事件,比如 scroll、drag、mouseover 这种。

然后是默认的优先级 NormalPriority、再就是低优先级 LowPriority,空闲优先级 IdlePriority。

lane模型

Untitled.png

图片from:神光 juejin.cn/post/717123…

react事件优先级

DiscreteEventPriority 离散事件优先级
ContinuousEventPriority 连续事件优先级
DefaultEventPriority 默认事件优先级
IdleEventPriority 空闲时间优先级

react 内部有 31 种 Lane 优先级,但是调度 Scheduler 任务的时候,会先转成事件优先级,然后再转成 Scheduler 的 5 种优先级。

  • 采用Lane 优先级的原因是因为,二进制位运算比较快

  • 然后转换成事件优先级是因为能和Scheduler 的 5 种优先级对应上

  • 为什么不直接转换成Schduler的五种优先级呢?

    • 因为 Schduler 是分离的一个包了,它的优先级机制也是独立的,而lane优先级和事件优先级都是react自己的一套优先级机制

总结

  1. 同步模式就是按顺序依次渲染
  2. 并发模式就是在 workLoop 里通过 shouldYield 的判断来打断渲染,之后把剩下的节点加入 Schedule 调度,来恢复渲染。
  3. 时间分片的 workLoop + 优先级调度,这就是 React 并发机制的实现原理。这就是 React 并发机制的实现原理

参考

全文都参考自:juejin.cn/post/717123…