为什么Async Await与React setState一起使用?

Leo*_*ant 27 babel async-await reactjs

我一直在使用异步等待我的ReactJS项目中的babel.我发现使用React setState很方便,我想更好地理解.考虑以下代码:

handleChange = (e) => {
  this.setState({[e.target.name]: e.target.value})
  console.log('synchronous code')
}

changeAndValidate = async (e) => {
  await this.handleChange(e)
  console.log('asynchronous validation code')
}

componentDidUpdate() {
  console.log('updated component')    
}
Run Code Online (Sandbox Code Playgroud)

我的目的是在组件更新后运行异步验证代码.它的工作原理!生成的控制台日志显示:

synchronous code
updated component
asynchronous validation code
Run Code Online (Sandbox Code Playgroud)

验证代码仅在handleChange更新状态并呈现新状态后运行.

通常在状态更新后运行代码,您必须在this.setState之后使用回调.这意味着如果你想在handleChange之后运行任何东西,你必须给它一个回调参数,然后传递给setState.不漂亮.但是在代码示例中,await知道在状态更新后,handleChange已经完成了...但我认为await只适用于promises并等待承诺在继续之前解决.在handleChange中没有承诺和解决方案......它怎么知道等待什么?

暗示似乎是setState以异步方式运行,并且await以某种方式知道它何时完成.也许setState在内部使用promises?

版本:

反应:"^ 15.4.2"

babel-core:"^ 6.26.0"

babel-preset-env:"^ 1.6.0",

babel-preset-react:"^ 6.24.1",

babel-preset-stage-0:"^ 6.24.1"

babel-plugin-system-import-transformer:"^ 3.1.0",

babel-plugin-transform-decorators-legacy:"^ 1.3.4",

babel-plugin-transform-runtime:"^ 6.23.0"

lin*_*new 34

我尽力简化和补充Davin的答案,这样你就可以更好地了解这里的实际情况:


  1. await放在this.handleChange之前,这将调度执行其余的changeAndValidate函数,使其仅在await 解析其右侧指定的值时运行,在本例中为this.handleChange返回的值
  2. await右边的this.handleChange执行:

    2.1.setState运行其更新程序,但因为setState不保证立即更新它可能会安排更新发生在以后的时间(无论是立即还是稍后,重要的是它是否已安排)

    2.2.console.log('同步代码')运行...

    2.3.this.handleChange然后退出返回undefined (返回undefined因为函数返回undefined,除非另有明确指定)

  3. await然后把这个未定义,因为它不是一个承诺它将它转换为一个已解决的promise,使用Promise.resolve(undefined)并等待它 - 它不会立即可用,因为它在幕后传递给它的.then方法是异步的:

"在完成当前运行的JavaScript事件循环之前,永远不会调用传递给promise的回调"

3.1.这意味着不确定会得到放入背面事件队列,(这意味着它现在是我们在事件队列中的setState更新背后...)

  1. 事件循环终于到达并获取我们的setState更新,现在执行...

  2. 事件循环到达并获取undefined,其值为undefined (如果需要,我们可以存储它,因此在等待前常用于存储已解析的结果)

    5.1.Promise.resolve()现在已经完成,这意味着等待不再有效,因此函数的其余部分可以恢复

  3. 您的验证代码运行

  • 所以这只是运气!:D (2认同)
  • 是一个好习惯还是我们可以依靠队列? (2认同)

Dav*_*yon 5

我还没有测试过这个,但这是我认为正在发生的事情:

undefined通过返回await后排队setState回调。Theawait正在做一个Promise.resolveunder(在regenerator-runtime),这反过来又将控制权交给事件循环中的下一个项目。

因此,setState回调恰好排在await.

您可以通过将 setTimeout(f => f, 0) 放在setState.

regenerator-runtimeinbabel本质上是一个Promise.resolve用于产生控制的循环。你可以看到_asyncToGenerator里面,它有一个Promise.resolve.

  • setTimeout 确实打破了它,但这是意料之中的。在任何承诺周围放置一个 setTimeout ,它会破坏异步等待,不是吗?但是,如果 setState 在所有其他情况下都像 async await 中的 promise,那么这仍然是一个可行的模式,不是吗? (2认同)
  • @LeoFabrikant `setState` 不像承诺。`await` 只是在 `setState` 回调之后排队。只有一个事件循环。 (2认同)