Skip to content

实现并发更新的交互部分

本节课代码地址(请参考课程获取) 要实现并发更新,需要做的改动包括: Lane 模型增加更多优先级 交互与优先级对应 调度阶段引入Scheduler ,新增调度策略逻辑 render 阶段可中断 根据update 计算state 的算法需要修改 扩展交互 思考一个问题:优先级从何而来? 答案:不同交互对应不同优先级。 可以根据「触发更新的上下文环境」赋予不同优先级。比如: 点击事件需要同步处理 滚动事件优先级再低点 ... 更进一步,还能推广到任何「可以触发更新的上下文环境」,比如: useEffect create 回调中触发更新的优先级 首屏渲染的优先级 下个问题:这些优先级的改动如何影响更新? 答案:只要优先级能影响update,就能影响更新。 当前我们掌握的与优先级相关的信息,包括: Scheduler 的5种优先级 React 中的Lane 模型 也就是说,运行流程在React 时,使用的是Lane 模型,运行流程在Scheduler 时,使用的 是优先级。所以需要实现两者的转换:

lanesToSchedulerPriority
schedulerPriorityToLane

# 扩展调度阶段

主要是在同步更新(微任务调度)的基础上扩展并发更新(Scheduler 调度),主要包括: 将Demo中的调度策略移到项目中 render 阶段变为「可中断」 梳理两种典型场景: 时间切片 高优先级更新打断低优先级更新

扩展state计算机制

扩展「根据lane对应update计算state」的机制,主要包括: 通过update 计算state 时可以跳过「优先级不够的update」 由于「高优先级任务打断低优先级任务」,同一个组件中「根据update计算state」的 流程可能会多次执行,所以需要保存update

跳过update需要考虑的问题

如何比较「优先级是否足够」? lane 数值大小的直接比较不够灵活。 如何同时兼顾「update的连续性」与「update的优先级」?

js
// u0
{

actionnum => num + 1,

lane: DefaultLane

}

// u1

{

action3,

lane: SyncLane

}

// u2

{

actionnum => num + 10,

lane: DefaultLane

}
// state = 0; updateLane = DefaultLane

//只考虑优先级情况下的结果:11

//只考虑连续性情况下的结果:13

新增baseState 、baseQueue 字段:

baseState 是本次更新参与计算的初始statememoizedState 是上次更新计算的

最终state

如果本次更新没有update 被跳过,则下次更新开始时baseState ===

memoizedState

如果本次更新有update 被跳过,则本次更新计算出的memoizedState 为「考虑优先 级」情况下计算的结果,baseState 为「最后一个没被跳过的update计算后的结 果」,下次更新开始时baseState !== memoizedState 本次更新「被跳过的update及其后面的所有update」都会被保存在baseQueue 中参 与下次state 计算 本次更新「参与计算但保存在baseQueue中的update」,优先级会降低到NoLane

js
// u0
{

actionnum => num + 1,

lane: DefaultLane

}

// u1

{

action3,

lane: SyncLane

}

// u2

{

actionnum => num + 10,

lane: DefaultLane

}

/*

*第一次render

* baseState = 0; memoizedState = 0;

* baseQueue = null; updateLane = DefaultLane;

*第一次render第一次计算

* baseState = 1; memoizedState = 1;

* baseQueue = null;

*第一次render第二次计算

* baseState = 1; memoizedState = 1;

* baseQueue = u1;

*第一次render第三次计算

* baseState = 1; memoizedState = 11;

* baseQueue = u1 -> u2(NoLane);

*/
/*

*第二次render

* baseState = 1; memoizedState = 11;

* baseQueue = u1 -> u2(NoLane); updateLane = SyncLane

*第二次render第一次计算

* baseState = 3; memoizedState = 3;

*第二次render第二次计算

* baseState = 13; memoizedState = 13;

*/

保存update的问题

考虑将update 保存在current 中。只要不进入commit 阶段,currentwip 不会互 换,所以保存在current 中,即使多次执行render 阶段,只要不进入commit 阶段,都能 从current 中恢复数据。 课后思考 TODO:扩展renderLane 的灵活性,将其扩展为renderLanes ,更好利用Lanes 这一数 据机构能够表示多种lane 的集合的能力。

用心学习,用代码说话 💻