Lazy loaded image
技术分享
🗒️协程在React中的应用
字数 2721阅读时长 7 分钟
2023-8-31
2023-10-9
type
status
date
slug
summary
tags
category
icon
password
字节前端面试的时候问我线程和进程的异同,进而问我有没有用过协程,我说没有。其实前端领域用到挺多协程的知识,当时没有复习到。

进程、线程和协程

notion image
 

进程

一个进程就是一个程序,它是资源分配的最小单位。同一时刻执行的进程数不会超过核心数。单核CPU为啥能执行多个进程?因为单核CPU可以极快地在进程间来回切换执行。

线程

线程能共享进程的大部分资源,线程和线程之间就好比进程内任务和任务之间的关系。它是程序执行过程中最小的单元。

协程

协程是一种轻量型的线程。协程调度切换时,将寄存器上下文和栈保存到其他地方,在切回来的时候,恢复先前保存的寄存器上下文和栈。协程的本质是个单线程,它不能同时将单个CPU的多个核用上,协程需要和进程配合才能运行在多CPU上。
执行体
拥有资源
调度
系统开销
通信
进程
资源分配的基本单位
线程的切换不会引起进程切换
开销远大于创建或撤销线程时的开销
借助IPC
线程
不拥有资源,线程可以访问隶属进程的资源
线程是独立调度的基本单位
切换时只需保存和设置少量寄存器内容,开销很小
直接读写同一进程中的数据进行通信
协程
不拥有资源,协程可以访问隶属线程的资源
调度完全由用户控制
上下文的切换非常快
直接读写同一线程中的数据进行通信

并发和并行

对于协程来说执行时是并行的,多个协程执行的关系并不是真正意义上的同时执行。

并行

对于单核CPU来说,在恰当的时机将CPU时间片分配给不同的任务。因为CPU执行速度极快,给人的感觉就是在同时执行两个任务。这样可以避免长任务长时间占据CPU,使后面的任务饥渴。
notion image

并发

并发针对多个CPU,多个CPU在同一时刻同时执行多个任务,是真正意义上的同时执行多个任务。
notion image

浏览器执行环境

JavaScript执行位置

从线程角度来看,浏览器执行环境可以分为以下几个线程:
  1. 主线程:主线程是浏览器中的核心线程,负责处理用户的输入、渲染页面、执行JavaScript代码等任务。在主线程中,JavaScript代码是单线程执行的,即一次只能执行一个任务,这也是JavaScript的特点之一。
  1. GUI线程:GUI线程负责渲染页面,包括解析HTML、CSS,构建DOM树和渲染页面等任务。GUI线程与主线程是互斥的,即在GUI线程执行时,主线程会暂停执行,等待GUI线程完成后再继续执行。
  1. 事件线程:事件线程负责处理用户的交互事件,如鼠标点击、键盘输入等。当用户触发一个事件时,事件线程会将该事件加入到事件队列中,等待主线程空闲时执行。
  1. 定时器线程:定时器线程负责处理定时器事件,如setTimeout和setInterval等。当定时器事件到达指定的时间时,定时器线程会将该事件加入到事件队列中,等待主线程空闲时执行。
  1. Web Worker线程:Web Worker线程是一个独立的线程,可以并行执行JavaScript代码,不会影响主线程的执行。Web Worker线程可以用于执行计算密集型的任务,如图像处理、数据分析等。
需要注意的是,JavaScript代码只能在主线程中执行,其他线程只能通过事件队列与主线程通信。当其他线程需要与主线程通信时,它们会将事件加入到事件队列中,等待主线程空闲时执行。因此,在编写JavaScript代码时,需要注意避免长时间的计算和阻塞主线程的执行,以免影响用户的体验。
JS线程就像单行道,JS解析、执行、绘制、事件处理都在这个线程上。任何一个任务执行事件过长,都会阻塞后面任务的执行。因此提出一种及时让出执行权的方法,协程就这样应运而生了。
notion image
 

协程在前端中的体现

在前端中,yield关键字可以用来暂停协程的执行,并将控制权交给调用方。yield操作返回一个值,这个值可以是任何JavaScript类型。调用方可以通过next方法来恢复协程的执行,并将一个值传递给协程。
下面是一个使用yield实现协程的例子:
在上面的例子中,使用Generator函数创建了一个协程,使用yield关键字来暂停协程的执行,并返回一个递增的数字。在每次调用next方法时,协程会从上一次暂停的位置继续执行,并返回下一个数字。
当然yield只是协程的一个应用。协程的调度,任务的定义,让出时间片的策略都可以由用户层来自定义,下面讲讲React中Fiber调度方法。

什么是Fiber?

Fiber原意是纤维的意思,注意别读错了,特别影响面试官心情。
notion image
Fiber架构在React中可以理解为:
Fiber架构 = Fiber节点 + Fiber调度算法

Fiber节点

在React中,每个Fiber节点都包含了一些指针,用于构成Fiber节点之间的链表,以及在调和过程中记录节点的状态和位置。下面是Fiber节点中常用的指针:
  1. child:指向当前节点的第一个子节点
  1. sibling:指向当前节点的下一个兄弟节点
  1. return:指向当前节点的父节点
这些指针构成了Fiber节点之间的链表关系,同时也记录了节点在组件树中的位置和状态,为React的调和过程提供了基础。
notion image
 

React的Fiber改造

在React中,组件树是由多个组件构成的,每个组件都有自己的状态和属性。当组件的状态或属性发生变化时,React会重新计算组件树,并将新的树与旧的树进行比较,以确定哪些节点需要更新。这个过程被称为调和(reconciliation)。
在React 15及其以下版本中,调和算法是基于递归的。当React需要更新组件树时,它会从根节点开始递归遍历整个树,找到需要更新的节点。这种算法虽然简单易懂,但是在组件树比较庞大时,会导致性能问题。
notion image
为了解决这个问题,React引入了Fiber节点。Fiber节点是一种轻量级的、可中断的执行单元,它可以在调和过程中暂停、恢复和重新启动。这意味着React可以在调和过程中优先处理一些高优先级的任务,而不必等待整个树的遍历完成。
notion image
Fiber节点的引入,使得React的调和算法更加高效和灵活,能够更好地应对复杂的组件树和高并发的场景。同时,Fiber节点也为React未来的发展提供了更多的可能性,例如实现React的异步渲染和增量更新等功能。

Fiber时间分片原理

requestIdleCallback

requestIdleCallback是浏览器提供的一个API,用于在浏览器空闲时执行任务。React利用了这个API实现了Fiber时间分片,从而提高了页面的响应性和流畅性。
requestIdleCallback的使用方法如下:
其中,callback是一个回调函数,用于在浏览器空闲时执行任务;options是一个可选的配置对象,用于设置任务的超时时间和优先级等。

执行流程

  1. 任务拆分:当React需要更新组件树时,它会将任务拆分成多个小任务,每个小任务的执行时间应该在16ms以内,以保证页面的响应性能。
  1. 任务优先级:React会为每个小任务设置一个优先级,高优先级的任务会优先执行,以保证页面的流畅性。React中共有5个优先级,分别是:
      • Immediate:最高优先级,用于处理紧急的任务,如用户输入等;
      • User Blocking:用户阻塞优先级,用于处理需要立即响应用户的任务,如动画等;
      • Normal:普通优先级,用于处理常规的任务,如组件的更新等;
      • Low:低优先级,用于处理不紧急的任务,如预加载等;
      • Idle:最低优先级,用于处理空闲时间的任务,如缓存清理等。
  1. 任务调度:React会根据任务的优先级,使用调度器来安排任务的执行顺序。调度器会根据当前任务的优先级和系统的空闲时间,来决定下一个要执行的任务。
  1. 中断恢复:当一个小任务执行了一段时间后,React会检查是否有更高优先级的任务需要执行。如果有,React会中断当前任务,并将任务状态保存到Fiber节点中,然后执行更高优先级的任务。当更高优先级的任务执行完毕后,React会恢复之前的任务,继续执行。
  1. 副作用收集:在任务执行过程中,React会记录所有的副作用,如DOM操作、网络请求等。当所有任务执行完毕后,React会根据副作用的类型和顺序,将它们合并成一个批次,然后一次性执行,以减少对DOM的操作次数。
Fiber时间分片的原理,是通过将一次耗时较长的任务拆分成多个小任务,然后根据任务的优先级和系统的空闲时间,来安排任务的执行顺序,以提高页面的响应性和流畅性。
notion image
 
 
上一篇
以太坊挖矿实践
下一篇
认证 (authentication) 和授权 (authorization) 的区别

评论
Loading...