从React Fiber到协程

从React Fiber到协程

Fiber是Facebook的React框架核心算法的2年重构的产物,Fiber reconciler。并且在React16中启用。

这里笔者默认读者都了解过React和Vue中都有的虚拟Dom(vnode)概念,同时也对这类框架的更新原理,diff算法有一定的认知。

简单来说,本文前置知识要求:

  • 前端基本知识,html,css,js
  • React
  • 虚拟dom/vnode
  • Diff算法

Fiber #

听说React团队在实现Fiber过程中考虑使用协程来实现,但是由于不适合/工程量太大而没有最终使用。

React在引入Fiber之前,由于其Diff算法需要递归去判断来对视图层做更新,就算实现了O(n)复杂度的Diff算法,但是由于其需要完成一次Diff才能更新视图层,所以导致当有递归层级非常深的组建树需要进行更新时,会产生用户感知非常明显的卡顿,这个问题就是Fiber解决的核心问题。

前端各种技术最根本的目的都是为了提高用户体验

React通过Fiber架构,让Reconcilation过程变成可被中断。‘适时’地让出CPU执行权,通过这种方式,能够让浏览器及时地响应用户的交互,用户拥有了更好的体验,编译器也有机会进行编译优化(JIT)及进行热代码优化。

实现Fiber的数据结构是链表,可以说,React升级到Fiber,就是一个数据结构课可能很常见做过的,递归转循环的过程。因为递归变成了循环,所以能够非常方便的回到顶层环境,找到上下文,对真实的dom节点进行更新。

在绝大部分语言中,通过将程序的循环结构变成递归,减少了很多次的函数调用,可以极大的提高程序的效率。

更多Fiber的概念,笔者可能讲的不如下方REF中讲的好,有兴趣可以直接前往查看。

协程 #

React的Fiber据笔者了解,并不是一个新的东西,而是有点类似参考了操作系统中时间分片,任务调度的思想进行设计的一个优化解决方案。当然前端当前许多的发展方向都是基于以往的系统,编程语言及后端所用到的的思想。

Fiber也称协程。笔者第一次接触这个概念是在学习go以及python的时候,当然很多语言都有这样的一个机制。

React Fiber的思想和协程的概念是契合的: React渲染的过程可以被中断,可以将控制权交回浏览器,让位给高优先级的任务,浏览器空闲后再恢复渲染。

如果希望更深入的理解Fiber,那就最好对协程有所了解。

协程是在线程基础上进一步的抽象细分,是单线程基础上的一种拓展。

正好整理一下目前已有的运行程式:

  • 进程: 系统分配资源的最小单位
  • 线程: CPU时间片分配的最小单位
  • 协程: 在线程基础上的异步逻辑单位

简单来说:一个进程可以有多个线程,一个线程占用一个cpu,一个线程可能有多个协程。

关于协程,笔者感觉下面这个Python的例子更加直观:

def consumer():
    r = ''
    while True:
        # 普通函数执行的过程中无法被中断和恢复,协程通过引入 async/await 或者 yield 等语法实现中断机制

        n = yield r     # 函数中断,等待收到信号r

        if not n:
            return
        print('[CONSUMER] Consuming %s...' % n)
        r = '200 OK'

def produce(c):
    c.send(None)        # 发送一个信号给 r
    n = 0
    while n < 3:
        n = n + 1
        print('[PRODUCER] Producing %s...' % n)
        r = c.send(n)   # 发送一个信号给 r
        print('[PRODUCER] Consumer return: %s' % r)
    c.close()

c = consumer()
produce(c)
# output:
# [PRODUCER] Producing 1...
# [CONSUMER] Consuming 1...
# [PRODUCER] Consumer return: 200 OK
# [PRODUCER] Producing 2...
# [CONSUMER] Consuming 2...
# [PRODUCER] Consumer return: 200 OK
# [PRODUCER] Producing 3...
# [CONSUMER] Consuming 3...
# [PRODUCER] Consumer return: 200 OK

看完协程,笔者其实想到了闭包这一概念,因为两者都保留了一个子函数的运行环境,都是单线程运行,可以被其他函数调用进入此环境。但是两者其实差别也很大,比如前者是能够进入函数运行的中间阶段,而后者是在一个函数的开头进入。

最后 #

站在巨人的肩膀上

计算机领域有很多社区,其中有很多优秀的项目和资料,笔者受益颇多,也希望能够继续从其中汲取,并也参与进行贡献。

本文只是简单的讲Fiber的思路和优点,同时对其设计思想的部分来源协程进行科普和对比,想深入了解请阅读下面不容错过的文章。

最后更新于: 2021-09-05

REF #