广告
返回顶部
首页 > 资讯 > 前端开发 > JavaScript >React 中的 setState 是同步还是异步
  • 415
分享到

React 中的 setState 是同步还是异步

2024-04-02 19:04:59 415人浏览 独家记忆
摘要

setState 是同步还是异步?肯定是异步的呀。 确定么?那看一下这段代码会打印什么: import { Component } from 'React'; class Dong

setState 是同步还是异步?肯定是异步的呀。

确定么?那看一下这段代码会打印什么:

import { Component } from 'React';
class Dong extends Component {
    constructor() {
        super();
        this.state = {
            count: 0
        }
    }
    componentDidMount() {
        setTimeout(() => {
            this.setState({
                count: 1
            });
            console.log(this.state.count);
            this.setState({
                count: 2
            });
            console.log(this.state.count);  
        });
    }
    render() {
        console.log('render:', this.state.count);
        return <div>{this.state.count}</div>;
    }
}

在 setTimeout 里修改了两次 state,并打印了 state 的值。如果是异步的,那应该打印的时候 count 还没修改,依然是 0,所以打印两次 0。然后初始化渲染一次,setState 后再渲染一次,应该 render 两次,count 分别为 0 和 2。按照异步的方式来分析,确实应该是这样的。

我们执行一下:

会发现两次打印分别是 1 和 2,也就是说 setState 同步修改了 state,然后每次都触发了渲染,所以一共 render 3 次,分别是 0、1、2。

那这么说 setState 是同步的?确定么?那看下这段代码会打印什么?

class Dong extends Component {
    constructor() {
        super();
        this.state = {
            count: 0
        }
    }
    componentDidMount() {
        this.setState({
            count: 1
        });
        console.log(this.state.count);
        this.setState({
            count: 2
        });
        console.log(this.state.count);
        this.setState({
            count: 3
        });
        console.log(this.state.count);
    }
    render() {
        console.log('render:', this.state.count);
        return <div>{this.state.count}</div>;
    }
}

如果 setState 是同步的,那执行完就会修改 state,应该分别打印 1、2、3,然后触发三次 render,加上最开始的一次,一共四次,打印 0、1、2、3。

我们来执行一下:

三次打印都是 0,这说明 setState 是异步的。而且三次 setState 只触发了一次 render,加上最开始的 render,一共两次,打印 0、3。

什么鬼,怎么又是异步的了?

而且不止 class 组件的 setState 是这样,换成 function 组件的 useState 也是一样的:

比如修改三次 state,只会 render 一次:

而在 setTimeout 里,每次修改 state 都会 render:

是不是有点晕,什么情况下 setState 是同步的,什么情况下是异步的呢?这要从源码找答案了,我们来读一下 setState 的源码。

首先理一下 React 渲染的流程:

React 渲染流程

react 通过 jsx 来描述界面,jsx 可以通过 babel 等编译器编译成 render function,然后执行后产生 vdom:

这个 vdom 也不是直接渲染的,而是会先转化为 fiber,之后再渲染。因为 vdom 里每个节点只记录了子节点(children),没有记录兄弟节点,所以必须一次性渲染完,不能打断。而转成 fiber 的链表结构就会记录父节点(return)、子节点(child)、兄弟节点(sibling),就变成了可打断的。

这里的 vdom 是 React Element 对象:

转化为 fiber 之后是 FiberNode 的对象:

从 vdom 转换成 fiber 的过程就叫做 reconcile,转换过程中会顺便创建对应的 dom 元素,然后全部转换完后一次性 commit 到 dom。这个过程不是一次性的,是通过 scheduler 调度执行的,那也就可以分批次进行,也就是可打断的含义。这就是 React 的 fiber 架构下的渲染流程。理论说完了,我们来对应到源码看一下(这里看的是 v17 的源码):

react 把 schedule 和 reconcile 叫做 render 阶段,这个阶段就是把 vdom 转为 fiber。(schedule 只是让 reconcile 可以分多次执行,可以打断,但做的事情是不变的,所以 schedule 也是 render 阶段的一部分)

之后把 fiber 更新到 dom 的过程就叫做 commit 阶段。

对应到源码里就是这样的:

这个 perfORMSyncWorkOnRoot 就是渲染的入口,就像之前所说的,会先执行 render 阶段,把 vdom 转成 fbier,然后再执行 commit,更新到 dom。

render 阶段会执行一个调度的 loop:

这个 loop 就是不断地处理一个个 fiber 的 reconcile:

每个节点都有 beginWork 和 completeWork 两个阶段,因为要做 vdom 转 fiber,而 vdom 是一个树形结构,需要递归处理:

具体不同节点的 reconcile 逻辑不同:

比如函数组件会被调用,拿到 render 出的 vdom 继续进行 reconcile:

比如 class 组件会创建实例,调用 render 方法,拿到 vdom,然后再继续 renconcileChildren。

总之,vdom 转 fiber 是一个递归进行的过程。之后再进行 commit 阶段。整个渲染流程的入口就是 performSyncWorkOnRoot 函数。渲染的流程讲完了,接下来就是 setState 怎么触发渲染的流程了:

setState 的流程

我们知道了渲染的入口就是 performSyncWorkOnRoot 函数,那 setState 修改完状态,触发一下这个函数不就行了?

确实是这样的。setState 会调用 dispathAction,创建一个 update 对象放到 fiber 节点的 updateQueue 上,然后调度渲染:

调度更新自然就是调度上面说的那个 performSyncWorkOnRoot 函数:react 会先从触发 update 的 fiber 往上找到根 fiber 节点,然后再调用 performSyncWorkOnRoot 的函数进行渲染:

这就是 setState 之后触发重新渲染的实现。而 setState 是同步还是异步,也就是在这一段控制的。我们看到判断条件里有个 excutionContext,这个是用来标识当前环境的,比如是批量还是非批量,是否执行过 render 阶段、commit 阶段。

其实在 ReactDOM.render 执行的时候会先调用 unbatchUpdates 函数:

这个函数会在 excutionContext 中设置一个 unbatach 的 flag:

这样在 update 的时候,就会立刻执行 performSyncWorkOnRoot 来渲染。因为首次渲染的时候是马上就要渲染的,没必要调度。

之后走到 commit 阶段会设置一个 commit 的 flag:

然后再次 setState 就不会走到 unbatch 的分支了。那为什么 setTimeout 里面的 setState 会同步执行呢?

因为直接从 setTimeout 执行的异步代码是没有设置 excutionContext 的,那就会走到 NoContext 的分支,会立刻渲染。

(这里的 flush 最终会调用 performSyncWorkOnRoot 函数来渲染):

有什么办法能让 setTimeout 里执行的函数也有 excutionContext 呢?其实 react17 暴露了 batchUpdates 的 api,用它包裹下,里面的 setState 就会批量执行了:

它的源码其实就是设置了下 excutionContext:

这样等 setState 全部执行完之后再 flush,调用 peformSyncWorkOnRoot 渲染,效果就是批量的 setState 了。其实按理来说 setState 不能叫异步,还是在同一个调用栈执行的,只不过顺序不同而已。只能叫批量还是非批量。

在 react17 中是这么处理的,如果是 react18,使用 createRoot 的 api 的话,就不会有这种问题了,就算是 setTimeout 里的代码也能批量执行,

而且为了兼容 react17 这种情况,还做了特殊处理,当没有开启并发模式,也就是还是用 ReactDOM.render 的 api 时,没有指定 excutionContext 还会立刻渲染:

等 react 18 普及以后,所有的 setState 都是批量的了,就不会再有批量还是非批量的问题。

总结

虽然我们讨论的是 setState 的同步异步,但这个不是 setTimeout、Promise 那种异步,只是指 setState 之后是否 state 马上变了,是否马上 render。

我们梳理了下 React 的渲染流程,包括 render 阶段、commit 阶段,render 阶段是从 vdom 转 fiber,包含 schedule 和 reconcile,commit 阶段是把 fiber 更新到 dom。渲染流程的入口是 performSyncWorkOnRoot 函数。setState 会创建 update 对象挂到 fiber 对象上,然后调度 performSyncWorkOnRoot 重新渲染。

在 react17 中,setState 是批量执行的,因为执行前会设置 executionContext。但如果在 setTimeout、事件监听器等函数里,就不会设置 executionContext 了,这时候 setState 会同步执行。可以在外面包一层 batchUpdates 函数,手动设置下 excutionContext 来切换成异步批量执行。

在 react18 里面,如果用 createRoot 的 api,就不会有这种问题了。setState 是同步还是异步这个问题等 react18 普及以后就不会再有了,因为所有的 setState 都是异步批量执行了。

到此这篇关于React 中的 setState 是同步还是异步的文章就介绍到这了,更多相关React setState 内容请搜索编程网以前的文章或继续浏览下面的相关文章希望大家以后多多支持编程网!

--结束END--

本文标题: React 中的 setState 是同步还是异步

本文链接: https://www.lsjlt.com/news/152404.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • React 中的 setState 是同步还是异步
    setState 是同步还是异步?肯定是异步的呀。 确定么?那看一下这段代码会打印什么: import { Component } from 'react'; class Dong ...
    99+
    2022-11-13
  • React中setState同步异步场景的使用
    目录setState同步异步场景描述原理保证内部数据统一启用并发更新参考setState同步异步场景 React通过this.state来访问state,通过this.setStat...
    99+
    2022-11-13
  • React中setState同步和异步的示例分析
    这篇文章主要介绍React中setState同步和异步的示例分析,文中介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们一定要看完! React起源于Facebook的内部项目。React的出现是革命性的创新,React的是一个...
    99+
    2023-06-15
  • react的setstate同步情况是什么
    本文小编为大家详细介绍“react的setstate同步情况是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“react的setstate同步情况是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习新知识吧。react...
    99+
    2023-07-04
  • React中setState的使用与同步异步的使用
    在react中,修改状态如果直接使用this.state,不会引起组件的重新渲染,需要通过 this.setState来对组件的属性进行修改。 1、this.setState的两种...
    99+
    2022-11-11
  • React setState异步原理是什么
    本文小编为大家详细介绍“React setState异步原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“React setState异步原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一起来学习...
    99+
    2023-07-04
  • 代码解析React中setState同步和异步问题
     React起源于Facebook的内部项目。React的出现是革命性的创新,React的是一个颠覆式的前端框架。在React官方这样介绍的它:一个声明式、高效、灵活的、创...
    99+
    2022-11-12
  • React中setState如何使用与如何同步异步
    这篇文章主要介绍“React中setState如何使用与如何同步异步”,在日常操作中,相信很多人在React中setState如何使用与如何同步异步问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”React中s...
    99+
    2023-06-14
  • React中setState同步或异步问题的示例分析
    这篇文章主要为大家展示了“React中setState同步或异步问题的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“React中setState同步或异步问题的示例分析”这篇文章吧。1....
    99+
    2023-06-25
  • 关于React中setState同步或异步问题的理解
    目录1. setState同步?异步? 2. 表现为异步 1. React 合成事件 2. 生命周期函数 3. 表现为同步 1. 原生事件 2. setTimeout 4. setS...
    99+
    2022-11-12
  • nodejs是同步还是异步io
    Node.js是一种基于Chrome V8引擎的JavaScript运行环境,它允许开发者使用JavaScript来编写服务器端代码。在Node.js中,I/O是一个核心概念,它非常重要,因为在服务器应用程序中,I/O操作往往是最常见的操作...
    99+
    2023-05-23
  • node.js中的forEach()是同步还是异步呢
    node里几乎所有用到回调函数的地方,都是异步的,回调函数后面的代码很可能比回调函数中的代码后先执行,特别是数据库操作。当然,node也提供了同步版本的函数,例如文件操作,fs.readFileSync()...
    99+
    2022-06-04
    node js forEach
  • react的setstate什么时候同步
    本教程操作环境:Windows10系统、react18.0.0版、Dell G3电脑。react的setstate什么时候同步?什么时候是异步的? 先给出答案: 有时表现出异步,有时表现出同步。setState只在合成事件和钩子函数中是“异...
    99+
    2023-05-14
    setstate React
  • vue页面渲染是同步还是异步
    本教程操作环境:windows7系统、vue3版,DELL G3电脑。vue页面渲染是异步的。Vue在更新DOM时是异步执行的,只要侦听到数据变化,Vue将开启一个队列,并缓冲在同一事件循环中发生的所有数据变更,如果同一个watcher被多...
    99+
    2023-05-14
    异步渲染 渲染 Vue
  • React 中 setState 的异步操作案例详解
    目录前言React 中的 setState 为什么需要异步操作?什么时候setState会进行同步操作?前言 在使用state的时候, 如果我们企图直接...
    99+
    2022-11-13
  • PHP接口重定向操作:同步还是异步?
    在开发Web应用程序时,接口重定向是一项非常常见的任务。接口重定向是指将请求从一个URL重定向到另一个URL。然而,在进行接口重定向时,我们需要考虑同步还是异步操作。在本文中,我们将探讨PHP接口重定向操作的同步和异步操作以及如何在Web...
    99+
    2023-07-03
    接口 重定向 同步
  • 同步还是异步?Python Spring 函数如何选择?
    在开发过程中,我们常常需要处理大量的数据,而这些数据处理需要消耗大量的时间。为了提高程序的效率,我们可以使用异步和同步两种处理方式来实现数据处理。 Python和Spring框架都提供了异步和同步的函数处理方式。在选择使用哪种方式时,我们...
    99+
    2023-06-22
    spring 函数 同步
  • React组件的创建与state同步异步方法是什么
    这篇文章主要介绍“React组件的创建与state同步异步方法是什么”,在日常操作中,相信很多人在React组件的创建与state同步异步方法是什么问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”React组件...
    99+
    2023-07-05
  • 同步还是异步?Java和Django并发处理的最佳实践
    随着互联网的发展,高并发成为了许多应用程序的必备特性。在这样的情况下,如何有效地处理并发请求成为了一个需要被解决的问题。Java和Django作为两种常用的编程语言,都有着自己的并发处理方式。那么,这两种语言的并发处理方式有什么区别呢?本...
    99+
    2023-09-10
    django 同步 并发
  • ajax同步异步指的是什么
    本篇内容主要讲解“ajax同步异步指的是什么”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“ajax同步异步指的是什么”吧! ajax...
    99+
    2022-10-19
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作