Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

setState为什么有时候表现为同步,有时候表现为异步? #4

Open
superdc opened this issue Apr 13, 2018 · 0 comments
Open
Labels
React React

Comments

@superdc
Copy link
Owner

superdc commented Apr 13, 2018

Q: setState为什么有时候表现为同步,有时候表现为异步?

setState

React组件根据propsstate渲染UI,props由父组件传递进子组件,state由组件内部来维护。

setState() schedules an update to a component’s state object. When state changes, the component responds by re-rendering.

React官方文档的这句话解释了setState的作用:setState可以用来更新一个组件的state对象,当state变化时,组件会重新渲染UI。

There is no guarantee that calls to setState will run synchronously, as they may eventually be batched together.

源码中对setState方法的注释:不能保证setState会同步执行,它们可能会进行批量更新。可以看下这个demo

记住一点:React为提升性能,采取批量更新的策略

setState流程

简单来说batchingStrategy.isBatchingUpdates影响着setState是同步还是异步。当组件处于批处理事务中时,其值为truesetState表现为异步;反之,为falsesetState表现为同步。

图中的internalInstance内部实例只组件在挂载过程中创建的ReactCompositeComponent实例。调用setState时,将partialState加入internalInstance的等待处理的状态列表,即_pendingStateQueue;如果传入了callback,则将callback加入internalInstance的等待处理的回调函数列表,即_pendingCallbacks。当batchingStrategy.isBatchingUpdatestrue即处理批处理事务中时,将内部实例加入dirtyComponents队列。React会在批处理事务关闭时对dirtyComponents中的组件进行批量更新,表现为异步;当batchingStrategy.isBatchingUpdatesfalse时,则会进行一个完整的更新过程,表现为同步。

setState表现为异步的情况

React会在自己可以控制的情况下采用批处理策略,比如如下几种情况

生命周期函数中调用setState

demo的中的代码来说:

  componentDidMount() {
    this.setState({
      num: this.state.num + 1
    });
    this.setState({
      num: this.state.num + 1
    });
  }

因为组件挂载采用了批处理策略,执行componentDidMount时,批处理事务仍然是打开的,那么此时batchingStrategy.isBatchingUpdatestrue,不会更新组件的state。上面的代码即相当于

internalInstance._pendingStateQueue.push(1,1)

那么当更新组件state时就把它更新为1了。

事件回调中调用setState

还有一种常见的setState方式,就是在事件回调中,请看这个demo
当我们点击按钮时,state仍然是每次只加1。

dispatchEvent: function(topLevelType, nativeEvent) {
    if (!ReactEventListener._enabled) {
      return;
    }
    var bookKeeping = TopLevelCallbackBookKeeping.getPooled(
      topLevelType,
      nativeEvent
    );
    try {
      ReactUpdates.batchedUpdates(handleTopLevelImpl, bookKeeping);
    } finally {
      TopLevelCallbackBookKeeping.release(bookKeeping);
    }
  }

React实现了自己的syntetic事件,当事件触发时会调用上面的方法,我们看到了ReactUpdates.batchedUpdates,在这里会打开批处理事务,那么在这期间我们不管调用多少次setState,都会将内部实例加入dirtyComponents中进行批量更新,那么就和上面的demo是一样的情况了。

setState表现为同步的情况

在浏览器的异步API和原生的DOM事件中setState会表现为同步,如setTimeout, setInterval, promise.then, addEventListener
因为浏览器是单线程,非阻塞的,主线程执行的是函数调用栈顶的执行上下文中的代码,当遇到异步API时,会将其上下文加入到任务队列或微任务队列中。当执行栈为空时,会先将微任务队列中的代码执行完,然后执行任务队列中的代码。所以,当在这些异步API的回调中调用setState时,组件不会处于批处理过程中,batchingStrategy.isBatchingUpdatesfalse,表现为同步。

@superdc superdc added the React React label Sep 8, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
React React
Projects
None yet
Development

No branches or pull requests

1 participant