专栏首页京东技术从中断机制看 React Fiber 技术

从中断机制看 React Fiber 技术

2860元腾讯云代金券免费领取,付款直接抵现金,立即领取>>>

腾讯云海外服务器1折限时抢购,2核4G云主机661元/3年,立即抢购>>>

腾讯云服务器1折限时抢购,2核4G云主机661元/3年,立即抢购>>>

Tech

前言

React16 开始,采用了 Fiber 机制替代了原有的同步渲染 VDOM 的方案,提高了页面渲染性能和用户体验。Fiber 究竟是什么,网上也很多优秀的技术揭秘文章,本篇主要想从计算机的中断机制来聊聊 React Fiber 技术大概工作原理。

01

单任务

在早期的单任务系统上,用户一次只能提交一个任务,当前运行的任务拥有全部硬件和软件资源,如果任务不主动释放 CPU 控制权,那么将一直占用所有资源,可能影响其他任务,造成资源浪费。该模式非常像当前浏览器运行模式,由于 UI 线程和 JS 线程的运行是互斥的,一旦 JS 长时间执行,浏览器无法及时响应用户交互,很容造成界面的卡顿,React 早期的同步渲染机制,当一次性更新的节点太多时,影响用户体验。

02

中断

中断最初是用于提高处理器效率的一种手段,在没有中断的情况下,当 CPU 在执行一段代码时,如果程序不主动退出(如:一段无限循环代码),那么 CPU 将被一直占用,影响其他任务运行。

while(true) {
  ...
};

而中断机制会强制中断当前 CPU 所执行的代码,转而去执行先前注册好的中断服务程序。比较常见的如:时钟中断,它每隔一定时间将中断当前正在执行的任务,并立刻执行预先设置的中断服务程序,从而实现不同任务之间的交替执行,这也是在多任务系统的重要的基础机制。中断机制主要通过硬件触发,CPU 属于被动接受。有了中断后,各任务执行时间就可以得到非常好的控制。

回到浏览器,目前浏览器大多是 60Hz(60 帧/秒),既每一帧耗时大概在 16ms 左右,它会经过下面这几个过程:

  1. 输入事件处理
  2. requestAnimationFrame
  3. DOM 渲染
  4. RIC (RequestIdleCallback)

我们除了在步骤 1-3 的中进行加塞外,无法进行任何干预,而步骤 4 的 RIC,算是一种防止多余计算资源被浪费的机制,例如,当一帧中步骤 1-3 只耗费 6ms,那么剩余 10ms 的计算资源则会被浪费,而 RIC 就是浏览器提供的一种资源利用的接口。RIC 非常像前面提到的“中断服务”,而浏览器的每一帧类似“中断机制”,利用它则可以在实现我们前面提到的大任务卡顿问题,例如:之前我们在 JS 中写如下代码时,无疑会阻塞浏览器渲染。

function task(){
  while(true){
   ...
  };
}
task();

但利用 RIC 机制后,我们完全可以让大任务周期性的执行,从而不阻止浏览器正常渲染。

将上面示例代码根据 RequestIdleCallback 进行调整,如下:

function task(){
  while(true){
   ...
  };
}
requestIdleCallback(task);

遗憾的是,由于我们的代码运行在用户态,无法感知到底层的真实中断,我们现在利用的 RIC 也只是一种中断的近似模拟,以上代码并不会在 16ms 到期后被强制中断,我们只能主动进行释放,将控制权交还浏览器,RIC 提供了 timeRemaining 方法,让任务知道主动释放时机,我们调整以上代码,如下:

function task(deadline){
  while(true){
   ...
   if(!deadline.timeRemaining()) {
     requestIdleCallback(task);
     // 主动退出循环,将控制权交还浏览器
     break;
   }
  };
}
requestIdleCallback(task);

以上示例,可以让一个大循环在“中断”机制下,不阻塞浏览器的渲染和响应。

注意: RIC 调用频率大概是 20 次/秒,远远低于页面流畅度的要求!这样每次你能得到差不多 50ms 的计算时间,如果完全用这 50ms 来做计算,同样会带来交互上的卡顿,所以 React Fiber 是基于自定义一套机制来模拟实现。

例如:setTimeout、setImmediate、MessageChannel。

以下是 React Fiber 中的主动释放片段代码:

function workLoop(hasTimeRemaining, initialTime) {
  let currentTime = initialTime;
  advanceTimers(currentTime);
  currentTask = peek(taskQueue);
  while (
    currentTask !== null &&
    !(enableSchedulerDebugging && isSchedulerPaused)
  ) {
    if (
      currentTask.expirationTime > currentTime &&
      (!hasTimeRemaining || shouldYieldToHost())
    ) {
      // 如果超时,则主动退出循环,将控制权交还浏览器
      break;
    }
    ...
  }
  ...
}

03

调度任务

有了中断机制,中断服务后,不同任务就能实现间断执行的可能,如何实现多任务的合理调度,就需要一个调度任务来进行处理,这通常代表着操作系统。例如,当一个任务 A 在执行到一半时,被中断机制强制中断,此时操作系统需要对当前任务 A 进行现场保护,如:寄存器数据,然后切换到下一个任务 B,当任务 A 再次被调度时,操作系统需要还原之前任务 A 的现场信息,如:寄存器数据,从而保证任务 A 能继续执行下一半任务。调度过程中如何保证被中断任务的信息不被破坏是一个非常重要的功能。

浏览器提供的 RIC 机制,类似“中断服务”注册机制,注册后我们只要合适的时机进行释放,就能实现“中断”效果,刚也提到对于不同任务之间切换,在中断后,需要考虑现场保护和现场还原。早期 React 是同步渲染机制,实际上是一个递归过程,递归可能会带来长的调用栈,这其实会给现场保护和还原变得复杂,React Fiber 的做法将递归过程拆分成一系列小任务(Fiber),转换成线性的链表结构,此时现场保护只需要保存下一个任务结构信息即可,所以拆分的任务上需要扩展额外信息,该结构记录着任务执行时所需要的必备信息:

{
    stateNode,
    child,
    return,
    sibling,
    expirationTime
    ...
}

我们看以下示例代码:

ReactDOM.render(
  <div id="A">
    A
    <div id="B">
      B<div id="C">C</div>
    </div>
    <div id="D">D</div>
  </div>,
  node
);

当 React 进行渲染时,会生成如下任务链,此时如果在执行任务 B 后时发现时间不足,主动释放后,只需要记录下一次任务 C 的信息,等再次调度时取得上次记录的信息即可。使用该机制后,对于渲染任务的优先级、撤销、挂起、恢复都能得到非常好的控制。

04

总结

中断机制其实是一种非常重要的解决资源共享的手段,对于操作系统而言,它已经是一个必不可少功能。随着浏览器的功能越来越强,越来越多功能也搬到了浏览器,如何保证用户在使用过程中的流畅,也是经常需要思考的问题,在业务开发过程中,我们可以根据实际场景利用好“中断机制”,提高用户体验。

推荐阅读

揭秘| 大数据计算引擎性能及稳定性提升神器!

"多模态数字内容生成"的技术探索与应用实践

AI新生:破解人机共存密码 | 每月一书(福利送书)

本文分享自微信公众号 - 京东技术(jingdongjishu),作者:凹凸曼-nobo

原文出处及转载信息见文内详细说明,如有侵权,请联系 [email protected] 删除。

原始发表时间:2021-03-26

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 这可能是最通俗的 React Fiber 打开方式

    作者:荒山 https://juejin.im/post/5dadc6045188255a270a0f85

    Nealyang
  • 完全理解React Fiber

    Fiber是对React核心算法的重构,2年重构的产物就是Fiber reconciler

    ayqy贾杰
  • 这可能是最通俗的 React Fiber 打开方式

    写一篇关于 React Fiber 的文章, 这个 Flag 立了很久,这也是今年的目标之一。最近的在掘金的文章获得很多关注和鼓励,给了我很多动力,所以下定决心...

    前端劝退师
  • react fiber 到底有多细

    Fiber 是对 React 核心算法的重构,facebook 团队使用两年多的时间去重构 React 的核心算法,在 React16 以上的版本中引入了 Fi...

    有赞coder
  • 六个问题让你更懂 React Fiber

    很多人都摸不透React,看不懂源码,甚至不想看源码(确实很难看懂啊!),"霸王硬上弓" 肯定是不行呀,不如从React的整体架构或者说从最核心的Fiber开始...

    Sneaker-前端公虾米
  • 从Context源码实现谈React性能优化

    Context的实现与组件的render息息相关。在讲解其实现前,我们先来了解render的时机。

    公众号@魔术师卡颂
  • Deep In React之浅谈 React Fiber 架构(一)

    2016 年都已经透露出来的概念,这都 9102 年了,我才开始写 Fiber 的文章,表示惭愧呀。不过现在好的是关于 Fiber 的资料已经很丰富了,在写文章...

    桃翁
  • Deep In React之浅谈 React Fiber 架构(一)

    2016 年都已经透露出来的概念,这都 9102 年了,我才开始写 Fiber 的文章,表示惭愧呀。不过现在好的是关于 Fiber 的资料已经很丰富了,在写文章...

    Nealyang
  • 干货 | React Fiber 初探

    携程技术
  • React Fiber源码分析 (介绍) React Fiber源码分析 第一篇React Fiber源码分析 第二篇(同步模式)React Fiber源码分析 第三篇(异步状态)

    写了分析源码的文章后, 总觉得缺少了什么, 在这里补一个整体的总结,输出个人的理解~

    菜的黑人牙膏
  • 探索 React 内核:深入 Fiber 架构和协调算法

    深入研究 React 称为 Fiber 的新架构,了解新 reconciliation 算法的两个主要阶段。

    童欧巴
  • [译] React Hooks 底层解析

    原文:https://medium.com/the-guild/under-the-hood-of-reacts-hooks-system-eb59638c9d...

    江米小枣
  • React新特性为啥产出这么慢?江郎才尽啦?

    作为前端领域最广为人知的技术之一,React2015年被「Jordan Walke」创造出来。

    公众号@魔术师卡颂
  • 前端领域的数据结构与算法解读 - fiber

    这一次我们顺着前面的内容,讲一些经典的数据结构与算法,本期我们来讲一下时下比较火热的 Reactfiber。

    lucifer210
  • React Hook 的底层实现原理

    https://medium.com/the-guild/under-the-hood-of-reacts-hooks-system-eb59638c9dba

    ConardLi
  • 【React】383- React Fiber:深入理解 React reconciliation 算法

    React 是一个用于构建用户交互界面的 JavaScript 库,其核心机制就是跟踪组件的状态变化,并将更新的状态映射到到新的界面。在 React 中,我们将...

    pingan8787
  • 手写系列-实现一个铂金段位的React

    为什么是铂金呢,因为和王者还有很远的距离。本文仅实现简单版本的 React,参考 React 16.8 的基本功能,包括虚拟 DOM、Fiber、Diff 算法...

    winty
  • react源码解析1.开篇介绍和面试题

    作为前端最常用的js库之一,熟悉react源码成了高级或资深前端工程师必备的能力,如果你不想停留在api的使用层面或者想在前端技能的深度上有所突破,那熟悉rea...

    全栈潇晨
  • react源码解析8.render阶段

    render阶段的主要工作是构建Fiber树和生成effectList,在第5章中我们知道了react入口的两种模式会进入performSyncWorkOnRo...

    全栈潇晨

扫码关注云+社区

领取腾讯云代金券

http://www.vxiaotou.com