四、Fiber 架构的心智模型
00 min
2024-7-26

React 核心团队成员Sebastion MarkBage(React Hooks 的发明者)曾说:我们在 React 中做的就是践行代数效应(AlgebraicEffects)。
那么,代数效应是什么呢?他和 React 有什么呢?
 
目录

代数效应


代数效应函数式编程中的一个概念,用于将副作用函数调用中分离。
接下来我们用虚拟的语法来解释.
假设我们有一个函数 getTotalPicNum,传入 2 个用户名称后,分别查找用户在平台保存的图片数量,最后将图片数量相加后返回。
getTotalPicNum 中我们不关注 getPicNum 的具体实现,只在乎“获取到两个数字后将他们相加的结果返回”这一过程。
接下来我们来实现 getPicNum
“用户在平台保存的图片数量”是保存在服务器中的。所以,为了获取该值,我们需要发起异步请求。
为了尽量保持 getTotalPicNum 的调用方式不变,我们首先想到了使用 async / await :
但是,使用 async / await 是有传染性的 —— 当一个函数变为 async 后,这意味着调用它的函数也需要是 async,这就破坏了 getTotalPicNum 的同步特性。
有没有什么办法能保持 getTotalPicNum 现有的调用方式不变的情况下实现异步请求呢?
没有。不过我们可以虚构一个。
我们虚构一个类似 try…catch 的语法—— try…handle 与两个操作符 performresume。
当执行到 getTotalPicNum 内部的 getPicNum 方法时,会执行 perform name
此时函数调用栈会从 getPicNum 方法内跳出,被最近一个 try…handle 捕获。类似于 throw Error 后被最近一个 try…catch 捕获
类似 throw ErrorError 会作为 catch 的参数,perform name 后面的 name 会作为 handle 的参数。
try…catch 最大的不同在于:当 Errorcatch 捕获之后,之前的调用栈就销毁了。而 handle 执行 resume 后会回到之前 perform 的调用栈。
对于 case ‘zhangsan’,执行完 resume with 230;后调用栈会回到 getPicNum ,此时 picNum === 230
注意 再次申明,try…catch 的语法是虚构的,只是为了演示 代数效应 的思想。
总结一下:代数效应能够将副作用(例子中为请求图片数量)从函数逻辑中分离,使函数关注点保持纯粹。
并且,从例子中可以看出,perform resume 不需要区分同步异步
 

代数效应在React中的应用


那么代数效应与 React 有什么关系呢?最明显的例子就是 Hooks
对于类似 useStateuseReduceruseRef 这样的 Hook,我们不需要FunctionComponentstateHook 中是如何保存的,React 会为我们处理。
所以我们只需要假设 useState 返回的是我们想要的 state,并编写业务逻辑就行了。
如果这个例子还不够明显,可以看看官方的 Suspense Demo🔗
DemoProfileDetails 用于展示用户名称。而用户名称是异步请求的。
但是 Demo 中完全是同步的写法。
 

代数效应与Generator


React15React16,协调器(Reconciler)重构的一大目的是:将老的同步的更新架构变为异步可中断的更新
异步可中断的更新可以理解为:更新在执行的过程中可能会被打断(浏览器时间切片用尽或者是有更高有的任务插队),当可以继续执行时恢复之前执行的中间状态。
这就是代数效应try…handle 的作用。
其实,浏览器原生就支持类似的实现,这就是 Generator
但是 Generator 的一些缺陷使 React 团队放弃了它:
  • 类似 asyncGanerator 也是有传染性的,使用 Generator 则上下文的其他函数也需要做出改变。这样的心智负担比较重。
  • Generator 执行的中间状态是上下文关联的
思考如下案例:
每当浏览器有空闲时间都会一次执行其中一个 doExpensiveWork,当时间耗尽则会中断,当再次恢复时会从中断位置继续执行。
如果只考虑”单一优先级任务的中断与继续“情况下 Generator 可以很好的实现异步可中断更新
但是当我们考虑”高优先级任务插队“的情况,如果此时我们完成 doExpensiveWorkAdoExpensiveWorkB 计算出 xy
此时 B 组件接收到一个高优更新,由于 Generator 执行的中间状态是上下文关联的,所以计算y 时无法复用之前已经计算出的 x,需要重新计算。
如果通过全局变量保存之前执行的中间状态,又会引入新的复杂度。
基于这些原因,React 没有采用 Generator 实现协调器
 

代数效应与 Fiber


Fiber 并不是计算机术语中的一个新名词,他的中文翻译叫做纤程,与进程(Process)、线程(Thread)、协程(Coroutine)同为程序执行过程。
在很多文章中将纤程理解为协程一种实现,在 JS 中,协程的实现便是 Generator
所以,我们可以将纤程(Fiber)、协程(Generator)理解为代数效应思想在 JS 中的体现。
 
React Fiber 可以理解为:
React 内部实现了的一套状态更新机制。支持任务不同优先级,可中断与恢复,并且恢复后可以复用之前的中间状态。
其中每个任务更新单元为 React Element 对应的 Fiber 节点。
 
上一篇
空白文章
下一篇
解密 React:探究背后的原理与源码