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

时间:2017-10-30 15:42:57

标签: reactjs async-await babel

我一直在我的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')    
}

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

synchronous code
updated component
asynchronous validation code

验证代码仅在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"

4 个答案:

答案 0 :(得分:25)

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

  1. 等待放在 this.handleChange 前面,这将安排执行剩余的 changeAndValidate 功能仅在等待 解析指定其右侧的值时运行,在本例中为 this.handleChange 返回的值
  2. this.handleChange ,在等待的右侧执行:

    2.1。 setState 运行其更新程序,但由于 setState 无法保证立即更新,因此可能会将更新安排在稍后的时间 (它没有'无论是直接的还是以后的,重要的是它的预定时间

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

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

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

  4.   

    “传递给promise的回调永远不会被调用   完成当前运行的JavaScript事件循环“

    3.1。这意味着未定义将被置于事件队列的后面,(这意味着它现在位于事件队列中的setState更新程序之后...)

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

    2. 事件循环到达并选择未定义,其评估为未定义 (如果我们需要,我们可以存储,因此=在等待存储已解决的结果之前常用的<)>

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

    3. 您的验证代码

答案 1 :(得分:4)

我还没有对此进行测试,但这是我认为正在发生的事情:

undefined返回的awaitsetState回调后排队。 await正在Promise.resolve下面(regenerator-runtime),这反过来会控制事件循环中的下一个项目。

因此,setState回调恰好在await之前排队,这是巧合。

您可以通过在setState周围放置一个setTimeout(f =&gt; f,0)进行测试。

regenerator-runtime中的

babel本质上是一个使用Promise.resolve来产生控制权的循环。您可以在_asyncToGenerator内看到它,Promise.resolve

答案 2 :(得分:1)

await的rv或返回值定义为:

rv
Returns the fulfilled value of the promise, or the value itself if it's not a Promise.

因为handleChange不是异步或承诺值,它只是返回自然值(在这种情况下,没有返回,所以undefined)。因此,这里没有异步事件循环触发器“让它知道handleChange已完成”,它只是按照你给它的顺序运行。

答案 3 :(得分:0)

  

setState()并不总是立即更新组件doc

但可能就是这种情况。

如果您想使用承诺替换回调,您可以自己实现:

setStateAsync(state) {
  return new Promise((resolve) => {
    this.setState(state, resolve)
  });
}

handleChange = (e) => {
  return this.setStateAsync({[e.target.name]: e.target.value})
}

参考:https://medium.com/front-end-hacking/async-await-with-react-lifecycle-methods-802e7760d802