setstate是同步还是异步_【前端面试】为什么 setState 是异步的

认识的初级阶段

要回答这个问题,我们可以先罗列几个现象。

比方说,你有一个 button,你给这个 button 绑定了一个 onclick,在这个 onclick 里面你调用了一个 setState,在这个 onclick 没有执行完毕之前,你改的 state 是不会真的被更新的。同时,当 onclick 没有执行完毕,render 也不会被触发。

再比方说,你的父组件传了一个 callback,目的是把父组件的 state 改变一下,callback 里面有 setState,当这个 callback 没有执行完毕前,这个 callback 里面的 state 也是不会更改的。

但是,setState 在一些场合是同步的,比如你给原生 dom 添加事件处理函数的时候,调用了 setState,调用完后,setState 是立即被执行的。

setState 改变了状态并触发了 render, 而 render 往往是伴随着重绘和回流的,显然,这是非常影响浏览器性能的操作。如果设计成同步的话,试想,假使多个组件绑定了一个合成事件处理函数,那么,当这个事件函数执行的时候,setState 会多次修改 state 并 render。我们不如在 setState 的时候先不触发,而是先把那些需要更改的组件标记一下,最后一起改变 state 并渲染,这对浏览器性能的提升显然是有积极的影响的。

现在,请把我上面讲的内容都忘掉,因为,那只是我在初始阶段的肤浅认识而已。

认识的中级阶段

这里我要补一个面试题里面常考的例子:

import React, { Component } from 'react';
class App extends Component {
    constructor(props) {
        super(props);
        this.state = { count:0 }
    }
    increase=()=>{
        this.setState({count:this.state.count+1});
        console.log(this.state.count);
        this.setState({count:this.state.count+1});
        console.log(this.state.count);
        setTimeout(()=>{
            this.setState({count:this.state.count+1});
            console.log(this.state.count);
            this.setState({count:this.state.count+1});
            console.log(this.state.count);
        })
    }
    render() { 
        return ( 
            <div>
                <div>setState</div>
                <button onClick={this.increase}>btn</button>
            </div>
         );
    }
}

输出结果是 0,0,2,3

我们肯定有两个疑惑,第一个疑惑是,最前面两个 setState count + 1 以后,为什么输出结果还是 0,第二个疑惑是,为什么 setTimeout 里面,第一个输出不是 3,毕竟不是已经加了三次吗?

其实整个过程是这样的。

onClick 是合成事件,其对应的函数 increase 中的 setState 会被放到 batch Update 的 queue 中做统一的更新,而且,当 state 中的属性名称相同时,后一次的 setState 会把上一次队列中的覆盖掉。但是,异步任务中的 setState 并不会被放到 batch Update 的 queue 里面,所以那里面的 setState 是同步的。

于是,整个过程是这样的。

1、第一个 setState 放到 batchUpdateQueue 里。输出两个 count都是 0

2、第二个 setState 即将放到 queue 里面时,看到里面已经有一个 key = count 的 setState 了,于是,把那个 setState 替换掉。

3、setTimeout 整个被放到事件循环队列的宏任务队列里面

4、合成事件函数结束

5、batchUpdateQueue 批量执行 setState,count 此时为 1,因为 queue 里面只有一个 setState({count:this.state.count+1})。

6、count 此时被更新为 1

7、宏任务执行,同步执行 setState。

8、输出 2,3

至于说 setState 为什么被设置为异步的,这是因为 react 的设计者想给那些不完全合格的设计者加一个强限制,假如说 setState 是同步的,那么每次 setState 都会调用 diff 算法去更新虚拟 dom。一个不合格的设计者在一个合成事件函数里面多次调用 setState,假如他被设计成同步的,一个合成事件函数被执行的时候,界面就会因为频繁操作虚拟 dom 而卡死。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值