Babel.js 为什么异步等待与React setState一起使用?

z0qdvdin  于 2022-12-16  发布在  Babel
关注(0)|答案(5)|浏览(165)

我在ReactJS项目中使用了async wait with 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更新了状态并呈现了新状态之后才运行。
通常要在state更新后运行代码,必须在this. setState之后使用回调。这意味着如果要在handleChange之后运行任何内容,必须为其给予一个回调参数,然后将其传递给setState。这并不美观。但在代码示例中,不知何故,wait知道handleChange在状态更新后完成...但是我认为wait只适用于承诺,并等待承诺解决后再继续。handleChange中没有承诺,也没有解决方案...它怎么知道该等待什么?
其含义似乎是setState异步运行,await不知何故知道它何时完成。也许setState在内部使用promise?
版本:
React:“^15.4.2”
巴伯尔-核心:“^6.26.0”
标签预设环境:“^1.6.0”,
Babel-预设-React:“^6.24.1”,
巴伯尔预设阶段0:“^6.24.1”
标签-插件-系统-导入-转换器:“^3.1.0”,
babel插件转换装饰器遗留:“^1.3.4”,
babel插件转换运行时:“^6.23.0”

ebdffaop

ebdffaop1#

我尽力简化和补充达文的回答,这样你就能更好地了解实际情况:

1.await放置在this.handleChange之前,这将调度changeAndValidate函数其余部分的执行,使其仅在await*解析它右侧指定的值时运行,在本例中,解析this.handleChange返回的值
1.wait右侧的this.handleChange执行:
2.1.setState运行其更新程序,但由于setState不保证立即更新,它可能会将更新计划在稍后时间进行***(立即更新或在稍后时间点更新并不重要,重要的是已计划更新)***
2.2. console.log(“同步代码”)运行...
2.3.this.handleChange然后退出,返回undefined*(返回undefined,因为函数返回undefined,除非另外显式指定)*
1.await然后接受这个undefined,由于它不是一个承诺,它使用Promise.resolve(undefined)将其转换为一个已解析的承诺,并等待它-它不是立即可用的,因为在后台它被传递给它的.then方法,这是异步的:
“在JavaScript事件循环的当前运行完成之前,将永远不会调用传入承诺的回调”
3.1.这意味着undefined将被放置在事件队列的后面,(这意味着它现在在事件队列中的setState更新器后面...)
1.事件循环最终到达并拾取我们的setState更新,该更新现在执行...
1.事件循环到达并拾取undefined,其计算结果为undefined*(如果需要,我们可以存储它,因此通常在await前面使用=来存储解析结果)*
5.1.Promise.resolve()现在已完成,这意味着await不再有效,因此函数的其余部分可以继续
1.您的验证代码运行

gpfsuwkq

gpfsuwkq2#

我还没有对此进行测试,但我认为这是正在发生的事情:
await返回的undefinedsetState回调之后排队。await正在下面(在regenerator-runtime中)执行Promise.resolve,这反过来又将控制权交给事件循环中的下一项。
因此,setState回调恰好排在await之前,这是巧合。
您可以通过在setState周围放置setTimeout(f =〉f,0)来测试这一点。
babel中的regenerator-runtime本质上是一个使用Promise.resolve产生控制的循环。你可以看到_asyncToGenerator内部,它有一个Promise.resolve

f87krz0w

f87krz0w3#

setState()并不总是立即更新组件文档
但这里可能就是这样。
如果你想用承诺来代替回调,你可以自己实现它:

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

9vw9lbht

9vw9lbht4#

await的rv或返回值定义为:

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

由于handleChange不是一个异步值或承诺值,它只是返回自然值(在本例中,没有返回,因此undefined).因此,这里没有异步事件循环触发器来“让它知道handleChange已完成”,它只是按照您给它的顺序运行.

qnyhuwrf

qnyhuwrf5#

举个简单的例子:在test-02中,代码await this.setState({ a: 10 });实际上,等于test-03中的this.setState({ a: 10 }); await undefined;。并且await undefined;导致其背后的代码在下一个事件循环中执行。

相关问题