网易首页 > 网易号 > 正文 申请入驻

前端培训:React调度算法迭代过程

0
分享至

React内部最难理解的地方就是「调度算法」,不仅抽象、复杂,还重构了一次。

可以说,只有React团队自己才能完全理解这套算法。

既然这样,那本文尝试从React团队成员的视角出发,来聊聊「调度算法」

什么是调度算法

React在v16之前面对的主要性能问题是:当组件树很庞大时,更新状态可能造成页面卡顿,根本原因在于:更新流程是「同步、不可中断的」

为了解决这个问题,React提出Fiber架构,意在「将更新流程变为异步、可中断的」

最终实现的交互流程如下:

  1. 不同交互产生不同优先级的更新(比如onClick回调中的更新优先级最高,useEffect回调中触发的更新优先级一般)
  2. 「调度算法」从众多更新中选出一个优先级作为本次render的优先级
  3. 以步骤2选择的优先级对组件树进行render

render过程中,如果又触发交互流程,步骤2又选出一个更高优先级,则之前的render中断,以新的优先级重新开始render

本文要聊的就是步骤2中的「调度算法」

expirationTime调度算法

「调度算法」需要解决的最基本的问题是:【关注尚硅谷,轻松学IT】如何从众多更新中选择其中一个更新的优先级作为本次render的优先级?

最早的算法叫做expirationTime算法

具体来说,更新的优先级与「触发交互的当前时间」「优先级对应的延迟时间」相关:

// MAX_SIGNED_31_BIT_INT为最大31 bit Interger

update.expirationTime = MAX_SIGNED_31_BIT_INT - (currentTime + updatePriority);

例如,高优先级更新u1、低优先级更新u2的updatePriority分别为0、200,则

MAX_SIGNED_31_BIT_INT - (currentTime + 0) > MAX_SIGNED_31_BIT_INT - (currentTime + 200)

// 即

u1.expirationTime > u2.expirationTime;

代表u1优先级更高。

expirationTime算法的原理简单易懂:每次都选出所有更新中「优先级最高的」

如何表示“批次”

除此之外,还有个问题需要解决:如何表示「批次」

「批次」是什么?考虑如下例子:

// 定义状态num

const [num, updateNum] = useState(0);

// ...某些修改num的地方

// 修改的方式1

updateNum(3);

// 修改的方式2

updateNum(num => num + 1);

两种「修改状态的方式」都会创建更新,区别在于:

  • 第一种方式,不需考虑更新前的状态,直接将状态num修改为3
  • 第二种方式,需要基于「更新前的状态」计算新状态

由于第二种方式的存在,更新之间可能有连续性。

所以「调度算法」计算出一个优先级后,组件render时实际参与计算「当前状态的值」的是:

「计算出的优先级对应更新」+「与该优先级相关的其他优先级对应更新」

这些相互关联,有连续性的更新被称为一个「批次」batch)。

expirationTime算法计算「批次」的方式也简单粗暴:优先级大于某个值(priorityOfBatch)的更新都会划为同一批次。

const isUpdateIncludedInBatch = priorityOfUpdate >= priorityOfBatch;

expirationTime算法保证了render异步可中断、且永远是最高优先级的更新先被处理。

这一时期该特性被称为Async Mode

IO密集型场景

Async Mode可以解决以下问题:

  1. 组件树逻辑复杂导致更新时卡顿(因为组件render变为可中断
  2. 重要的交互更快响应(因为不同交互产生更新优先级不同)

这些问题统称为CPU密集型问题

在前端,还有一类问题也会影响体验,那就是「请求数据造成的等待」。这类问题被称为IO密集型问题

为了解决IO密集型问题的,React提出了Suspense。考虑如下代码:

const App = () => {

const [count, setCount] = useState(0);

useEffect(() => {

const t = setInterval(() => {

setCount(count => count + 1);

}, 1000);

return () => clearInterval(t);

}, []);

return (

loading...}>

count is {count}

其中:

  • 每过一秒会触发一次更新,将状态count更新为count => count + 1
  • Sub中会发起异步请求,请求返回前,包裹SubSuspense会渲染fallback

假设请求三秒后返回,理想情况下,请求发起前后UI会依次显示为:

// Sub内请求发起前

I am sub, count is 0

count is 0

// Sub内请求发起第1秒

loading...

count is 1

// Sub内请求发起第2秒

loading...

count is 2

// Sub内请求发起第3秒

loading...

count is 3

// Sub内请求成功后

I am sub, request success, count is 4

count is 4

从用户的视角观察,有两个任务在并发执行:

  1. 请求Sub的任务(观察第一个div的变化)
  2. 改变count的任务(观察第二个div的变化)

Suspense带来了「多任务并发执行」的直观感受。

因此,Async Mode(异步模式)也更名为Concurrent Mode(并发模式)。

一个无法解决的bug

那么Suspense对应更新的优先级是高还是低呢?

当请求成功后,合理的逻辑应该是「尽快展示成功后的UI」。所以Suspense对应更新应该是高优先级更新。那么,在示例中共有两类更新:

  1. Suspense对应的高优IO更新,简称u0
  2. 每秒产生的低优CPU更新,简称u1u2u3

expirationTime算法下:

// u0优先级远大于u1、u2、u3...

u0.expirationTime >> u1.expirationTime > u2.expirationTime > …

u0优先级最高,则u1及之后的更新都需要等待u0执行完毕后再进行。

u0需要等待「请求完毕」才能执行。所以,请求发起前后UI会依次显示为:

// Sub内请求发起前

I am sub, count is 0

count is 0

// Sub内请求发起第1秒

loading...

count is 0

// Sub内请求发起第2秒

loading...

count is 0

// Sub内请求发起第3秒

loading...

count is 0

// Sub内请求成功后

I am sub, request success, count is 4

count is 4

从用户的视角观察,第二个div被卡住了3秒后突然变为4。

所以,只考虑CPU密集型场景的情况下,「高优更新先执行」的算法并无问题。

但考虑IO密集型场景的情况下,高优IO更新会阻塞低优CPU更新,这显然是不对的。

所以expirationTime算法并不能很好支持并发更新。

expirationTime算法在线Demo

出现bug的原因

expirationTime算法最大的问题在于:expirationTime字段耦合了「优先级」「批次」这两个概念,限制了模型的表达能力。

这导致高优IO更新不会与低优CPU更新划为同一「批次」。那么低优CPU更新就必须等待高优IO更新处理完后再处理。

如果不同更新能根据实际情况灵活划分「批次」,就不会产生这个bug

重构迫在眉睫,并且重构的目标很明确:将「优先级」「批次」拆分到两个字段中。

Lane调度算法

新的调度算法被称为Lane,他是如何定义「优先级」「批次」呢?

对于优先级,一个lane就是一个32bit Interger,最高位为符号位,所以最多可以有31个位参与运算。

不同优先级对应不同lane,越低的位代表越高的优先级,比如:

// 对应SyncLane,为最高优先级

0b0000000000000000000000000000001

// 对应InputContinuousLane

0b0000000000000000000000000000100

// 对应DefaultLane

0b0000000000000000000000000010000

// 对应IdleLane

0b0100000000000000000000000000000

// 对应OffscreenLane,为最低优先级

0b1000000000000000000000000000000

「批次」则由lanes定义,一个lanes同样也是一个32bit Interger,代表「一到多个lane的集合」

可以用位运算很轻松的将多个lane划入同一个批次

// 要使用的批次

let lanesForBatch = 0;

const laneA = 0b0000000000000000000000001000000;

const laneB = 0b0000000000000000000000000000001;

// 将laneA纳入批次中

lanesForBatch |= laneA;

// 将laneB纳入批次中

lanesForBatch |= laneB;

上文提到的Suspensebug是由于expirationTime算法不能灵活划定批次导致的。

lanes就完全没有这种顾虑,任何想划定为同一「批次」优先级(lane)都能用位运算轻松搞定。

http://www.atguigu.com

特别声明:以上内容(如有图片或视频亦包括在内)为自媒体平台“网易号”用户上传并发布,本平台仅提供信息存储服务。

Notice: The content above (including the pictures and videos if any) is uploaded and posted by a user of NetEase Hao, which is a social media platform and only provides information storage services.

相关推荐
热点推荐
斯基拉:本泽马转会利雅得新月达成,合约期至2027年

斯基拉:本泽马转会利雅得新月达成,合约期至2027年

懂球帝
2026-02-02 15:32:28
中国人的恐怖“期待”,西方怎么也想不明白,老外都吓傻了!

中国人的恐怖“期待”,西方怎么也想不明白,老外都吓傻了!

毛豆论道
2026-02-01 17:43:14
具俊晔为大S雕像取名《熙媛的永恒轨道》,设计过程全部公开

具俊晔为大S雕像取名《熙媛的永恒轨道》,设计过程全部公开

素素娱乐
2026-02-02 17:03:27
凌晨时分,美资金耗尽了,中国巨幅清除美债,特朗普通告全国4字

凌晨时分,美资金耗尽了,中国巨幅清除美债,特朗普通告全国4字

命运自认幽默
2026-02-02 15:12:35
真不识货!被易立弃用的2名球员,如今在新东家,都成了球队大腿

真不识货!被易立弃用的2名球员,如今在新东家,都成了球队大腿

金山话体育
2026-02-02 09:42:25
郑爽抱娃街头与张恒家互撕!满头白发穿搭邋遢,孩子哭到发抖太扎心

郑爽抱娃街头与张恒家互撕!满头白发穿搭邋遢,孩子哭到发抖太扎心

八星人
2026-02-02 13:29:47
金银大跌,摩根大通分析师:别慌!上涨势头还会持续,年底仍看至6300

金银大跌,摩根大通分析师:别慌!上涨势头还会持续,年底仍看至6300

华尔街见闻官方
2026-02-02 14:31:11
30岁男子如厕时猝死,妻子回忆事发前5天,那些被掩盖的生命求救信号

30岁男子如厕时猝死,妻子回忆事发前5天,那些被掩盖的生命求救信号

红星新闻
2026-02-01 23:06:17
《太平年》5个帝王结局凄惨:最惨的非石重贵、钱弘倧,是刘承祐

《太平年》5个帝王结局凄惨:最惨的非石重贵、钱弘倧,是刘承祐

暖心萌阿菇凉
2026-02-02 16:52:34
吴君如很早就说过了,陈妍希私下就是这样穿

吴君如很早就说过了,陈妍希私下就是这样穿

八星人
2026-01-21 15:14:26
为了得到“魔兽”霍华德,桃园云豹究竟付出了多大的代价?

为了得到“魔兽”霍华德,桃园云豹究竟付出了多大的代价?

罗氏八卦
2026-02-02 20:35:03
“黑色星期一”!全球股市齐跌,纳指期货1%,韩国股指跌5%,英伟达降温AI预期,金、银遭遇历史性踩踏

“黑色星期一”!全球股市齐跌,纳指期货1%,韩国股指跌5%,英伟达降温AI预期,金、银遭遇历史性踩踏

华尔街见闻官方
2026-02-02 13:59:42
章小蕙在国外太敢穿了!一袭蕾丝裙秀出尤物身材,完全没有大婶味

章小蕙在国外太敢穿了!一袭蕾丝裙秀出尤物身材,完全没有大婶味

蓓小西
2026-02-01 10:07:12
众星在大S雕像前拍照,小S双手合十感谢外界,悼念致辞全场哽咽

众星在大S雕像前拍照,小S双手合十感谢外界,悼念致辞全场哽咽

萌神木木
2026-02-02 15:28:15
令人细思极恐的“被室友拒绝3次”事件:永远不要低估人性的丑

令人细思极恐的“被室友拒绝3次”事件:永远不要低估人性的丑

另子维爱读史
2026-02-02 18:06:13
个人增值税起征点提高至1000元

个人增值税起征点提高至1000元

南方都市报
2026-02-02 07:05:17
涉嫌严重违纪违法,姜雨林被查

涉嫌严重违纪违法,姜雨林被查

都市快报橙柿互动
2026-02-02 20:44:25
歌手于文文演唱会突然晕倒,被救护车紧急送医!吴克群救场:“她少唱的,我来帮她唱”

歌手于文文演唱会突然晕倒,被救护车紧急送医!吴克群救场:“她少唱的,我来帮她唱”

黄河新闻网吕梁频道
2026-02-02 09:12:32
被戴8次绿帽子,3次被捉奸在床,这就是我们“玉女”守卫的爱情?

被戴8次绿帽子,3次被捉奸在床,这就是我们“玉女”守卫的爱情?

素衣读史
2026-01-30 17:15:38
孩子私卖长辈金镯被金店1.7万元回收,家长半年后发现要求退还遭拒绝,市监局和警方介入调查

孩子私卖长辈金镯被金店1.7万元回收,家长半年后发现要求退还遭拒绝,市监局和警方介入调查

极目新闻
2026-02-02 13:58:31
2026-02-03 03:16:49
IT爱好者小尚
IT爱好者小尚
分享IT教育类信息
630文章数 55关注度
往期回顾 全部

科技要闻

阿里筑墙,腾讯寄生,字节偷家

头条要闻

周生生足金挂坠戴1天被刮花 检测后发现含铁、银、钯

头条要闻

周生生足金挂坠戴1天被刮花 检测后发现含铁、银、钯

体育要闻

澳网男单决赛,属于阿尔卡拉斯的加冕仪式

娱乐要闻

57岁音乐人袁惟仁去世,家属发文悼念

财经要闻

金银暴跌 全球股市遭遇“黑色星期一”

汽车要闻

雷克萨斯LC500将于今年底停产 "最美雷克萨斯"谢幕

态度原创

家居
时尚
手机
健康
游戏

家居要闻

现代几何彩拼 智焕童梦居

裤子+靴子:今年冬天最经典搭配,松弛又时髦!

手机要闻

消息称三星Galaxy S27 Ultra将搭载更安全的Polar ID面部识别技术

耳石症分类型,症状大不同

涨价后没人买了!Xbox在日本崩盘:年销仅3万台

无障碍浏览 进入关怀版