当前位置: 首页 > 科技观察

面试官:“宝子,setState 是同步还是异步的呀?”

时间:2023-03-21 19:40:14 科技观察

面试官:“包子,setState是同步的还是异步的?”转载本文请联系Gogo前端世界公众号。这次就带大家了解一下React中常见的setState原理。setState本身的默认行为在进入正题之前,你肯定需要学习一下React的基本用法。如果没有,请点赞离开;如果你会用React,请点赞收藏并留下(●'?'●)。我们在使用React的时候,经常会用到state(废话),但是能够完全理解setState的帅哥美女真的没几个。毕竟,程序员不太可能像我这样知识渊博(而且长得好看)。那么,要想弄清楚,你应该转世(整容)吗?不,您需要先弄清楚setState本身的默认行为。其实也很简单。我们都知道setState可以以对象的形式传递状态,也可以以函数的形式传递。不管状态是对象形式还是函数形式,它都会先保存所有状态,然后合并状态,所有状态合并后进行一次性DOM更新。如果状态是对象的形式,后面的状态会直接覆盖前面的状态。类似于Object.assign()的合并操作。对于对象的state,我们邀请了Cuihua上传代码:运行代码,Dom中显示的结果为1。显然,这两个setState只有一个生效。真的吗?其实两次都生效了,只是两个setState在执行前合并为一个。不能说最后哪个生效了,可以说两个都没生效,因为合并后的代码最后执行了。如果状态是函数的形式,则依次调用函数来累积状态。所有函数调用完成后,得到最终状态,最后进行一次一次性的DOM更新。翠华,这里又是一段代码……明显不同的结果可以说明执行了两次,因为函数状态不会合并,而是相应地运行。嗯,翠花可??以先下去休息了,不过前面已经整理好了,那么setState的研究是不是就结束了呢?当然不是。接下来,我们就换个场地继续战斗吧。setState是同步的还是异步的在面试场景中,只要是和React相关的,面试官肯定会舔着你的脸问你:“宝贝,setState是同步的还是异步的?”。面对如此不要脸的困难,我们需要明确一点,从API层面来说,是正常调用执行的函数,自然是同步API。所以,这里所说的同步和异步是指调用API后DOM是同步更新还是异步更新。来来来,我们请娜塔莎贴出代码……果然,外国妹子提的代码实在是太难消化了。通过结果,我们发现了一个很奇怪的现象:第一个事件执行明显是异步的,先打印出来。两个0,Dom会变为1;第二次也是异步的,但是我们发现多次执行没有效果(异步?);第三次同步执行;我们吸毒了吗?看着我的向日葵书戳破它。先说结论,首先,同步和异步主要看调用的环境。如果在React可以控制的范围内调用setState,则它是异步的。比如合成事件处理函数和生命周期函数,此时会进行批量更新,即状态合并后进行DOM更新。如果在本机JavaScript控件的范围内调用setState,则它是同步的。例如,在原生的事件处理函数、定时器回调函数、Ajax回调函数中,调用setState后DOM会立即更新。为什么是这样?其实我们看到的所谓“异步”就是开启了“批量更新”模式。批量更新模式可以减少真实DOM渲染的次数,所以只要在React的控制范围内,出于性能考虑就一定是批量更新模式。批量更新会先合并状态,然后做一次性的DOM更新。那么如果没有批量更新呢?从生命周期的角度来看,每一个setState都是一个完整的更新过程,其中包含了包括re-render在内的很多操作。大致流程如下:shouldComponentUpdate->componentWillUpdate->render->componentDidUpdate;re-render本身涉及到对DOM的操作,会带来较大的性能开销。如果“一次setState触发一个完整的更新过程”的结论成立,那么每一次setState调用都会触发一次重新渲染,我们的view很可能会卡死几次没有刷新,渲染会出现如下流程:因此,setState异步(或批量更新)的一个重要动机是避免频繁重新渲染。在实际的Reactruntime中,setState的异步实现有点类似于浏览器中的Event-Loop:每次来一个setState,就塞进一个队列中。时机成熟时,合并队列中的状态结果,最后只对最新的状态值进行更新过程。此过程称为“批量更新”。批量更新的过程如下代码中的箭头流程图所示:只要我们的同步代码还在执行,“入队”这个动作就不会停止。因此,即使我们在React中写了N次setState循环,也只是增加了state任务入队的次数,并不会带来频繁的re-renders。当N次调用结束后,只是状态的任务队列的内容发生了变化,状态本身并不会立即发生变化。为了让大家更好的吃到Natasha,哦不,是Natasha端上来的食物,我帮你整理了一下setState的执行流程图:当然,这张流程图你可能看不懂(多傻啊),没关系,下面会有更多。如果是非批量更新方式,调用setState多少次就会渲染多少次真实DOM,性能低下。但是在某些情况下,我们需要对JS控制的区域实现批量更新(异步更新DOM),那么应该怎么做呢?强制批量更新其实很简单,不好意思说这么容易,因为这东西简直TM的easy。我们只需要将代码包裹在unstable_batchedUpdates方法的回调函数中,就可以实现强制批量更新。具体使用方法也很简单,直接从react-dom中导入,然后把代码放到调用函数中即可。(翠花和娜塔莎结婚了,我给你上传代码)到现在,我们已经是完美的一对了,啊呸~