Skip to content

Latest commit

 

History

History
94 lines (62 loc) · 7.01 KB

File metadata and controls

94 lines (62 loc) · 7.01 KB

scheduler&Lane

课程地址 https://xiaochen1024.com/courseware/60b1b2f6cf10a4003b634718/60b1b556cf10a4003b634727

Scheduler

我们知道如果我们的应用占用较长的js执行时间,比如超过了设备一帧的时间,那么设备的绘制就会出不的现象。

Scheduler 主要的功能是时间切片和调度优先级,react在对比差异的时候会占用一定的js执行时间,Scheduler内部借助MessageChannel 实现了在浏览器绘制之前指定一个时间片,如果react在指定时间内没对比完,Scheduler就会强制交出执行权给浏览器

时间切片performUnitOfWork 之后会执行 render阶段commit阶段,如果在浏览器的一帧中,cpu的计算还没完成,就会让出js执行权给浏览器,这个判断在 workLoopConcurrent 函数中,shouldYield 就是用来判断剩余的时间有没有用尽。在源码中每个时间片时5ms,这个值会根据设备的fps调整。

任务的暂停shouldYield 函数中有一段,所以可以知道,如果当前时间大于任务开始的时间+ yieldInterval ,就打断了任务的进行。

调度优先级 ​在 Scheduler 中有两个函数可以创建具有优先级的任务

runWithPriority:以一个优先级执行callback,如果是同步的任务,优先级就是 ImmediateSchedulerPriority 有五种优先级 ImmediatePriority UserBlockingPriority NormalPriority LowPriority IdlePriority

scheduleCallback: 以一个优先级注册callback,在适当的时机执行,因为涉及过期时间的计算,所以 scheduleCallbackrunWithPriority 的粒度更细。

  • scheduleCallback 中优先级意味着过期时间,优先级越高 priorityLevel 就越小,过期时间离当前时间就越近,var expirationTime = startTime + timeout; 例如IMMEDIATE_PRIORITY_TIMEOUT=-1,那 var expirationTime = startTime + (-1); 就小于当前时间了,所以要立即执行。
  • scheduleCallback 调度的过程用到了 小顶堆 ,所以我们可以在 O(1) 的复杂度找到 优先级最高的task ,不了解可以查阅资料,在源码中小顶堆存放着任务,每次 peek 都能取到离过期时间最近的task。
  • scheduleCallback 中,未过期任务task 存放在 timerQueue 中,过期任务 存放在 taskQueue 中。新建 newTask任务 之后,判断 newTask 是否过期,没过期就加入timerQueue 中。如果此时 taskQueue 中还没有过期任务,timerQueue 中离过期时间最近的task正好是newTask,则设置个定时器,到了过期时间就加入taskQueue中。当timerQueue 中有任务,就取出最早过期的任务执行。

任务暂停之后怎么继续 ​ 在 performConcurrentWorkOnRoot 函数的结尾有这样一个判断,如果 callbackNode 等于 originalCallbackNode 那就恢复任务的执行

Lane

Lane 的和 Scheduler 是两套优先级机制,相比来说 Lane 的优先级粒度更细,Lane 的意思是车道,类似赛车一样,在 task 获取优先级时,总是会优先抢内圈的赛道,Lane 表示的优先级有以下几个特点。

1.可以表示不同批次的优先级。​ 从代码中中可以看到,每个优先级都是个31位二进制数字,1表示该位置可以用,0代表这个位置不能用,从第一个优先级NoLanes到OffscreenLane优先级是降低的,优先级越低1的个数也就越多(赛车比赛外圈的车越多),也就是说含多个1的优先级就是同一个批次。

export const NoLanes: Lanes = /*                        */ 0b0000000000000000000000000000000;
export const NoLane: Lane = /*                          */ 0b0000000000000000000000000000000;
  
export const SyncLane: Lane = /*                        */ 0b0000000000000000000000000000001;
export const SyncBatchedLane: Lane = /*                 */ 0b0000000000000000000000000000010;
  
export const InputDiscreteHydrationLane: Lane = /*      */ 0b0000000000000000000000000000100;
const InputDiscreteLanes: Lanes = /*                    */ 0b0000000000000000000000000011000;
  
const InputContinuousHydrationLane: Lane = /*           */ 0b0000000000000000000000000100000;
const InputContinuousLanes: Lanes = /*                  */ 0b0000000000000000000000011000000;
  
export const DefaultHydrationLane: Lane = /*            */ 0b0000000000000000000000100000000;
export const DefaultLanes: Lanes = /*                   */ 0b0000000000000000000111000000000;
  
const TransitionHydrationLane: Lane = /*                */ 0b0000000000000000001000000000000;
const TransitionLanes: Lanes = /*                       */ 0b0000000001111111110000000000000;
  
const RetryLanes: Lanes = /*                            */ 0b0000011110000000000000000000000;
  
export const SomeRetryLane: Lanes = /*                  */ 0b0000010000000000000000000000000;
  
export const SelectiveHydrationLane: Lane = /*          */ 0b0000100000000000000000000000000;
  
const NonIdleLanes = /*                                 */ 0b0000111111111111111111111111111;
  
export const IdleHydrationLane: Lane = /*               */ 0b0001000000000000000000000000000;
const IdleLanes: Lanes = /*                             */ 0b0110000000000000000000000000000;
  
export const OffscreenLane: Lane = /*                   */ 0b1000000000000000000000000000000;

2.优先级的计算的性能高

​ 例如,可以通过二进制按位与来判断a和b代表的lane是否存在交集

export function includesSomeLane(a: Lanes | Lane, b: Lanes | Lane) {
  return (a & b) !== NoLanes;
}

Lane模型中task是怎么获取优先级的(赛车的初始赛道) ​任务获取赛道的方式是从高优先级的 lanes 开始的,这个过程发生在 findUpdateLane 函数中,如果高优先级没有可用的 lane 了就下降到优先级低的 lanes 中寻找,其中pickArbitraryLane 会调用 getHighestPriorityLane 获取一批 lanes 中优先级最高的那一位,也就是通过 lanes & -lanes 获取最右边的一位

Lane模型中高优先级是怎么插队的(赛车抢赛道) ​在Lane模型中如果一个低优先级的任务执行,并且还在调度的时候触发了一个高优先级的任务,则高优先级的任务打断低优先级任务,此时应该先取消低优先级的任务,因为此时低优先级的任务可能已经进行了一段时间,Fiber树 已经构建了一部分,所以需要将 Fiber树 还原,这个过程发生在函数 prepareFreshStack 中,在这个函数中会初始化已经构建的 Fiber树

Lane模型中怎么解决饥饿问题(最后一名赛车最后也要到达终点啊) ​在调度优先级的过程中,会调用 markStarvedLanesAsExpired 遍历 pendingLanes(未执行的任务包含的lane),如果没过期时间就计算一个过期时间,如果过期了就加入root.expiredLanes 中,然后在下次调用 getNextLane 函数的时候会优先返回 expiredLanes。随之时间的推移,低优先级的任务被插队,最后也会变成高优先级的任务