daily-interview / fe-interview

:smiley: 每日一道经典前端面试题,一起共同成长。
https://blog.csdn.net/u010494753
MIT License
173 stars 22 forks source link

setState到底是异步还是同步? #38

Open artdong opened 5 years ago

artdong commented 5 years ago

setState到底是异步还是同步?

artdong commented 5 years ago

setState()更新状态的2种写法

1、setState(updater, [callback]), updater为返回stateChange对象的函数: (state, props) => stateChange 接收的state和props被保证为最新的。

2、setState(stateChange, [callback]) stateChange为对象, callback是可选的回调函数, 在状态更新且界面更新后才执行。

总结: 对象方式是函数方式的简写方式。如果新状态不依赖于原状态 ,使用对象方式;如果新状态依赖于原状态,使用函数方式;如果需要在setState()后获取最新的状态数据, 在第二个callback函数中读取

class App extends Component {
 this.state = { count: 0 }
 componentDidMount() {
   this.setState({val: 1}, () => {
            // eslint-disable-next-line no-console
            console.log(this.state.count); // 输出更新后的值 --> 1
    });
 }
}

setState()更新状态是异步还是同步的?

1、setState只在合成事件和钩子函数中是“异步”的,在原生事件、setTimeout和promise中都是同步的。

setTimeout

class App extends Component {
 this.state = { count: 0 }
 componentDidMount() {
    setTimeout(() => {
      this.setState({ count: this.state.count+ 1 })
      console.log(this.state.count) // 输出更新后的值 --> 1
    }, 0)
 }
}

promise

class App extends Component {
 this.state = { count: 0 }
 componentDidMount() {
   Promise.resolve().then(() => {
         this.setState({val: this.state.val + 1});  
          console.log('promise', this.state.val);  // 输出更新后的值 --> 1
  });
}

2、setState 的“异步”并不是说内部由异步代码实现,其实本身执行的过程和代码都是同步的,只是合成事件和钩子函数的调用顺序在更新之前,导致在合成事件和钩子函数中没法立马拿到更新后的值,形成了所谓的“异步”,当然可以通过第二个参数 setState(partialState, callback) 中的callback拿到更新后的结果。

3、setState 的批量更新优化也是建立在“异步”(合成事件、钩子函数)之上的,在原生事件和setTimeout 中不会批量更新,在“异步”中如果对同一个值进行多次setState,setState的批量更新策略会对其进行覆盖,取最后一次的执行,如果是同时setState多个不同的值,在更新时会对其进行合并批量更新。