Javascript 为什么reactjs Async 中的setState 是Async 而不是Sync?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/36085726/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-23 18:36:55  来源:igfitidea点击:

Why is setState in reactjs Async instead of Sync?

javascriptmultithreadingasynchronousreactjs

提问by Anup

I have just found that in react this.setState()function in any component is asynchronous or is called after the completion of the function that it was called in.

我刚刚发现this.setState()在任何组件中的 react函数都是异步的,或者在调用它的函数完成后被调用。

Now I searched and found this blog (setState() State Mutation Operation May Be Synchronous In ReactJS)

现在我搜索并找到了这个博客(setState() State Mutation Operation May Be Synchronous In ReactJS

Here he found that setStateis async(called when stack is empty) or sync(called as soon as called) depending on how the change of state was triggered.

在这里他发现setState是 async(当堆栈为空时调用)或同步(在调用时立即调用)取决于状态更改的触发方式。

Now these two things are hard to digest

现在这两件事很难消化

  1. In the blog the setStatefunction is called inside a function updateState, but what triggered the updateStatefunction is not something that a called function would know about.
  2. Why would they make setStateasync as JS is single threaded language and this setState is not a WebAPI or server call so has to be done on JS's thread only. Are they doing this so that Re-Rendering does not stop all the event listeners and stuff, or there is some other design issue.
  1. 在博客中,setState函数是在函数内部调用的updateState,但是触发updateState函数的不是被调用函数知道的。
  2. 他们为什么要setState异步,因为 JS 是单线程语言,而且这个 setState 不是 WebAPI 或服务器调用,所以只能在 JS 的线程上完成。他们这样做是为了使重新渲染不会停止所有事件侦听器和其他东西,还是存在其他一些设计问题。

回答by JoeTidee

You can call a function after the state value has updated:

您可以在状态值更新后调用函数:

this.setState({foo: 'bar'}, () => { 
    // Do something here. 
});

Also, if you have lots of states to update at once, group them all within the same setState:

此外,如果您有很多状态要一次更新,请将它们全部分组在同一个中setState

Instead of:

代替:

this.setState({foo: "one"}, () => {
    this.setState({bar: "two"});
});

Just do this:

只需这样做:

this.setState({
    foo: "one",
    bar: "two"
});

回答by Sachin

1) setStateactions are asynchronous and are batched for performance gains. This is explained in the documentation of setState.

1)setState动作是异步的,并且被批处理以提高性能。的文档中对此进行了解释setState

setState() does not immediately mutate this.state but creates a pending state transition. Accessing this.state after calling this method can potentially return the existing value. There is no guarantee of synchronous operation of calls to setState and calls may be batched for performance gains.

setState() 不会立即改变 this.state 而是创建一个挂起的状态转换。调用此方法后访问 this.state 可能会返回现有值。无法保证对 setState 调用的同步操作,并且可能会批量调用以提高性能。


2) Why would they make setState async as JS is a single threaded language and this setStateis not a WebAPI or server call?


2)为什么他们要使 setState 异步,因为 JS 是单线程语言,这setState不是 WebAPI 或服务器调用?

This is because setStatealters the state and causes rerendering. This can be an expensive operation and making it synchronous might leave the browser unresponsive.

Thus the setState calls are asynchronous as well as batched for better UI experience and performance.

这是因为setState改变状态并导致重新渲染。这可能是一项昂贵的操作,并且使其同步可能会使浏览器无响应。

因此 setState 调用是异步的,并且是批处理的,以获得更好的 UI 体验和性能。

回答by gillyb

I know this question is old, but it has been causing a lot of confusion for many reactjs users for a long time, including me. Recently Dan Abramov (from the react team) just wrote up a great explanation as to why the nature of setStateis async:

我知道这个问题很老,但长期以来它一直给许多 reactjs 用户造成很多困惑,包括我。最近 Dan Abramov(来自 react 团队)刚刚写了一个很好的解释,解释了为什么setStateasync的本质:

https://github.com/facebook/react/issues/11527#issuecomment-360199710

https://github.com/facebook/react/issues/11527#issuecomment-360199710

setStateis meant to be asynchronous, and there are a few really good reasons for that in the linked explanation by Dan Abramov. This doesn't mean it will alwaysbe asynchronous - it mainly means that you just can't dependon it being synchronous. ReactJS takes into consideration many variables in the scenario that you're changing the state in, to decide when the stateshould actually be updated and your component rerendered.
A simple example to demonstrate this, is that if you call setStateas a reaction to a user action, then the statewill probably be updated immediately (although, again, you can't count on it), so the user won't feel any delay, but if you call setStatein reaction to an ajax call response or some other event that isn't triggered by the user, then the state might be updated with a slight delay, since the user won't really feel this delay, and it will improve performance by waiting to batch multiple state updates together and rerender the DOM fewer times.

setState是异步的,在 Dan Abramov 的链接解释中有几个非常好的理由。这并不意味着它总是异步的——它主要意味着你不能依赖它是同步的。ReactJS 会考虑您正在更改状态的场景中的许多变量,以决定何时state实际更新和重新渲染您的组件。
一个简单的例子来证明这一点,如果你调用setState作为对用户操作的反应,那么它state可能会立即更新(尽管你不能指望它),所以用户不会感觉到任何延迟,但如果你打电话setState响应 ajax 调用响应或其他一些不是由用户触发的事件,那么状态可能会稍微延迟更新,因为用户不会真正感受到这种延迟,它将通过等待来提高性能一起批处理多个状态更新并减少重新渲染 DOM 的次数。

回答by zloctb

Good article here https://github.com/vasanthk/react-bits/blob/master/patterns/27.passing-function-to-setState.md

这里的好文章 https://github.com/vasanthk/react-bits/blob/master/patterns/27.passing-function-to-setState.md

// assuming this.state.count === 0
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
this.setState({count: this.state.count + 1});
// this.state.count === 1, not 3

Solution
this.setState((prevState, props) => ({
  count: prevState.count + props.increment
}));

or pass callback this.setState ({.....},callback)

或通过回调 this.setState ({.....},callback)

https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b

https://medium.com/javascript-scene/setstate-gate-abc10a9b2d82 https://medium.freecodecamp.org/functional-setstate-is-the-future-of-react-374f30401b6b

回答by Ярослав

You can use the following wrap to make sync call

您可以使用以下包装进行同步调用

this.setState((state =>{
  return{
    something
  }
})

回答by supi

Imagine incrementing a counter in some component:

想象一下在某个组件中增加一个计数器:

  class SomeComponent extends Component{

    state = {
      updatedByDiv: '',
      updatedByBtn: '',
      counter: 0
    }

    divCountHandler = () => {
      this.setState({
        updatedByDiv: 'Div',
        counter: this.state.counter + 1
      });
      console.log('divCountHandler executed');
    }

    btnCountHandler = () => {
      this.setState({
        updatedByBtn: 'Button',
        counter: this.state.counter + 1
      });
      console.log('btnCountHandler executed');
    }
    ...
    ...
    render(){
      return (
        ...
        // a parent div
        <div onClick={this.divCountHandler}>
          // a child button
          <button onClick={this.btnCountHandler}>Increment Count</button>
        </div>
        ...
      )
    }
  }

There is a count handler attached to both the parent and the child components. This is done purposely so we can execute the setState() twice within the same click event bubbling context, but from within 2 different handlers.

有一个计数处理程序附加到父组件和子组件。这是故意完成的,所以我们可以在同一个点击事件冒泡上下文中执行 setState() 两次,但是在 2 个不同的处理程序中。

As we would imagine, a single click event on the button would now trigger both these handlers since the event bubbles from target to the outermost container during the bubbling phase.

正如我们想象的那样,按钮上的单个单击事件现在将触发这两个处理程序,因为在冒泡阶段事件从目标冒泡到最外层容器。

Therefore the btnCountHandler() executes first, expected to increment the count to 1 and then the divCountHandler() executes, expected to increment the count to 2.

因此 btnCountHandler() 首先执行,期望将计数增加到 1,然后 divCountHandler() 执行,期望将计数增加到 2。

However the count only increments to 1 as you can inspect in React Developer tools.

然而,当您可以在 React Developer 工具中检查时,计数只会增加到 1。

This proves that react

这证明反应

  • queues all the setState calls

  • comes back to this queue after executing the last method in the context(the divCountHandler in this case)

  • merges all the object mutations happening within multiple setState calls in the same context(all method calls within a single event phase is same context for e.g.) into one single object mutation syntax (merging makes sense because this is why we can update the state properties independently in setState() in the first place)

  • and passes it into one single setState() to prevent re-rendering due to multiple setState() calls (this is a very primitive description of batching).

  • 将所有 setState 调用排队

  • 在执行完上下文中的最后一个方法后返回到这个队列(在这种情况下是 divCountHandler)

  • 将在同一上下文中的多个 setState 调用中发生的所有对象突变(例如,单个事件阶段中的所有方法调用都是相同的上下文)合并为一个单一的对象突变语法(合并是有意义的,因为这就是我们可以独立更新状态属性的原因在 setState() 中)

  • 并将其传递给一个 setState() 以防止由于多次 setState() 调用而重新渲染(这是对批处理的一种非常原始的描述)。

Resultant code run by react:

反应运行的结果代码:

this.setState({
  updatedByDiv: 'Div',
  updatedByBtn: 'Button',
  counter: this.state.counter + 1
})

To stop this behaviour, instead of passing objects as arguments to the setState method, callbacks are passed.

为了阻止这种行为,不是将对象作为参数传递给 setState 方法,而是传递回调。

    divCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByDiv: 'Div',
              counter: prevState.counter + 1
            };
          });
          console.log('divCountHandler executed');
        }

    btnCountHandler = () => {
          this.setState((prevState, props) => {
            return {
              updatedByBtn: 'Button',
              counter: prevState.counter + 1
            };
          });
      console.log('btnCountHandler executed');
    }

After the last method finishes execution and when react returns to process the setState queue, it simply calls the callback for each setState queued, passing in the previous component state.

在最后一个方法执行完成后,当 react 返回处理 setState 队列时,它只是为每个排队的 setState 调用回调,传入之前的组件状态。

This way react ensures that the last callback in the queue gets to update the state that all of its previous counterparts have laid hands on.

通过这种方式,react 确保队列中的最后一个回调可以更新其所有先前的对应对象已经处理过的状态。

回答by Manohar Reddy Poreddy

Yes, setState() is asynchronous.

是的,setState() 是异步的。

From the link: https://reactjs.org/docs/react-component.html#setstate

来自链接:https: //reactjs.org/docs/react-component.html#setstate

  • React does not guarantee that the state changes are applied immediately.
  • setState() does not always immediately update the component.
  • Think of setState() as a request rather than an immediate command to update the component.
  • React 不保证状态更改会立即应用。
  • setState() 并不总是立即更新组件。
  • 将 setState() 视为更新组件的请求而不是立即命令。

Because they think
From the link: https://github.com/facebook/react/issues/11527#issuecomment-360199710

因为他们认为
来自链接:https: //github.com/facebook/react/issues/11527#issuecomment-360199710

... we agree that setState() re-rendering synchronously would be inefficient in many cases

...我们同意 setState() 同步重新渲染在许多情况下效率低下

Asynchronous setState() makes life very difficult for those getting started and even experienced unfortunately:
- unexpected rendering issues: delayed rendering or no rendering (based on program logic)
- passing parameters is a big deal
among other issues.

异步 setState() 使那些刚开始甚至不幸经历过的人的生活变得非常困难:
- 意外的渲染问题:延迟渲染或不渲染(基于程序逻辑)
- 传递参数是
其他问题中的一个大问题。

Below example helped:

下面的例子有帮助:

// call doMyTask1 - here we set state
// then after state is updated...
//     call to doMyTask2 to proceed further in program

constructor(props) {
    // ..

    // This binding is necessary to make `this` work in the callback
    this.doMyTask1 = this.doMyTask1.bind(this);
    this.doMyTask2 = this.doMyTask2.bind(this);
}

function doMyTask1(myparam1) {
    // ..

    this.setState(
        {
            mystate1: 'myvalue1',
            mystate2: 'myvalue2'
            // ...
        },    
        () => {
            this.doMyTask2(myparam1); 
        }
    );
}

function doMyTask2(myparam2) {
    // ..
}

Hope that helps.

希望有帮助。