react源码学习-实现篇-优先级

优先级

什么是优先级
生命周期方法:同步执行。

受控的用户输入:比如输入框内输入文字,同步执行。

交互事件:比如动画,高优先级执行。

其他:比如数据请求,低优先级执行。

如何调度优先级

Schedule提供了runWithPriority方法,

在这里插入图片描述

接受一个优先级常量和一个回调函数作为参数。回调函数会以优先级高低为顺序排列在一个定时器中并在合适的时间触发。

在这里插入图片描述

Schedule对优先级的定义,值越低优先级越高。

下面可以通过一个demo来了解Schedule的流程。

[一文搞懂调度Schedule][https://blog.csdn.net/lin_fightin/article/details/122829688?spm=1001.2014.3001.5502]

例子

优先级最终会反映到update.lane变量上,当前我们只需要知道该变量能够区分update的优先级。

比如新产生了一个update为u1,他负责将黑色主题变为白色主题,他的优先级比较低。

u1先触发,进入render阶段,其优先级比较低,执行时间比较长。此时当前fiber的updateQueue为

fiber.updateQueue = {
  baseState: {
    blackTheme: 'true', //黑色主题
    text: "H"
  },
  firstBaseUpdate: null,
  lastBaseUpdate: null,
  shard: {
    pending: u1
  }
  effects: null
}

在u1完成render阶段前,用户输入了I,想将H变为I,产生了新的update,称为u2,u2的优先级高于u1,所以在workLoopConcurrent函数执行的时候,shouleYield返回true的时候,表示当前桢无剩余时间,需要下一桢才能继续干活的时候,会继续调度,找到了刚才刚注册调度的关于u2的performConcurrentWorkOnRoot的注册函数被调度,优先级更高,所以中断之前u1的工作,开始执行u2的performConcurrentWorkOnRoot。

此时fiber对应

fiber.updateQueue.shared.pending === u2 ----> u1
                                     ^        |
                                     |________|
// 即
u2.next === u1;
u1.next === u2;

在u2的render阶段,在processUpdateQueue方法中,shared.pending环状链表呗剪开拼接到了baseUpdate后面。所以此时update的执行顺序应该是u1 – u2

由于u2优先级更高,所以u1被跳过,又担心update之前可能有依赖关系,所以被跳过的update及后面的update会成为下次更新的baseUpdate,即u1 – u2

最终u2完成了render-commit阶段,先将H变为了I,此时fiber

fiber.updateQueue = {
	baseState: {
		blackTheme: true,
		text: "I"
	},
	firstBaseUpdate: u1,
	lastBaseUpdate: u2,
	shared: {pending: null},
	effects: null
}

在commit阶段结尾,会在调度一次更新,在该次更新中,基于baseState中firstBaseUpdate保存的u1,开启了render阶段。最终两次update都完成的结果如下:

fiber.updateQueue = {
  baseState: {
    blackTheme: false,
    text: 'HI'
  },
  firstBaseUpdate: null,
  lastBaseUpdate: null
  shared: {
    pending: null
  },
  effects: null
};

可以发现,u2对应的update在render阶段执行了两次,相应的render阶段的生命周期钩子也会触发两次,这也是为什么这些钩子会被标记unsafe_,因为可中断的异步更新可能导致某些钩子执行多次。

如何保证状态正确

1 render阶段可能被中断,如何保证updateQueue中保存的update不丢失。

2 当前状态可能依赖前一个状态,如何支持跳过低优先级的同时保证状态依赖的连续性。

1 保证update不丢失

在render阶段执行update的时候,也就是processUpdateQueue函数,hared.pending的环被剪开并连接在updateQueue.lastBaseUpdate后面。

在这里插入图片描述

实际上shared.pending会被同时连接在workInProgress updateQueue.lastBaseUpdatecurrent updateQueue.lastBaseUpdate后面。

当render中断的时候,会基于current fiber的updateQueue克隆,所以不会丢失。

当高优先级的任务commit阶段完成渲染的时候,由于workInprogress updateQueue.lastBaseUpdate克隆了current updateQueue.lastBaeUpdate的值,保存了上一次的Update,所以当workInprogress fiber树 变为current fiber树

的时候也不会造成Update丢失。

2 保证状态的连续性

之前说过,当某个update因为优先级跳过的时候,该update和他之后所有的update会形成链表,存放在fiber.updateQueue的firstBaseUpdate上。

如有四个update, A1 B2 C1 D2 ,1的优先级比2高,所以A1, C1先执行,B2跳过。

baseState: ''
shared.pending : A1 => B2 => C1 => D2

第一次render的时候

baseState: ''
baseUpdate: null
render阶段使用的update [A1, C1]
memoizedState: "AC"

执行了A1和C1。第二次render的时候

baseState: 'A',
baseUpdate: B2 -> C1 -> D2
render阶段使用的update [B2, C1, D2]
memoizedState: "ABCD"

这里的baseState并不等于上一次更新的memoizedState,这是由于b2被跳过了,当有update跳过的时候,下次的baseState!=上次更新的memoizedState

在执行update的processUpdateQueue可以看到

在这里插入图片描述

对于执行优先级较高的update,当已经有update被跳过的时候,当前update需要clone一份保存。

至于baseState不等于上一次的memoizedState,逻辑在这里

在这里插入图片描述

在这里插入图片描述

从上可以看到,如果遇到了有需要跳过的update,不仅会保存他的update,还会保存当时queue.baseState作为下一次的newState。

当所有update执行完毕的时候,如果newLastBaseUpdate有值,那么就不会执行newBaseState = newState这个操作,那么queue.baseState = newBaseState永远是老的state。以上面的为例:

执行A1 --->   不需要跳过,newLastBaseUpdate === null  -> newBaseState = A(最新的state)
执行B2 ---> 	B2需要跳过,newLastBaseUpdate === B2  --> newBaseState = queue.baseState等于A. 
执行C1 --->   C1不需要跳过,但B2跳过了,需要clone一份,newLastBaseUpdate === B2->C1 ---> newBaseState = queue.baseState还是A ---> queue.baseState = newBaseState = A1
执行D2 --->    D2需要跳过, newLastBaseUpdate === B2->C1->D2 ----> newBaseState ===queue.baseState还是A

do while循环结束后,newLastBaseUpdate有值,不将最新的state赋值,然后 queue.baseState = newBaseState 就等于A。
这就是为什么第二次render的时候baseState是A而不是AC
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

coderlin_

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值