此异步等待不应该工作。为啥它会起作用?

Posted

技术标签:

【中文标题】此异步等待不应该工作。为啥它会起作用?【英文标题】:This Async Await should not be working. Why is it working then?此异步等待不应该工作。为什么它会起作用? 【发布时间】:2021-12-30 02:44:14 【问题描述】:

所以我已经做了几个月的 React-Native 了,我偶然发现了一个很大的块必须在 setState 函数中更新的情况。之后我不得不调用一个依赖于该状态的函数。 当然,状态并没有立即发生变化,因为我了解到 setState 是异步的。

这是我的结构

父母

stateSetter = (data) => 
        this.setState(
            ...data
        )
    
dateFilter = () => 
        let startDate = this.state.startDate
        let endDate = this.state.endDate
        if (startDate !== "" && endDate == "") 
            this.setState(
                DataFiltered: this.state.Data.filter(elem => Helper.convertDateToISO(elem.date)>= Helper.convertDateToISO(startDate))
            )
        else if(startDate == "" && endDate !== "")
            this.setState(
                DataFiltered: this.state.Data.filter(elem => Helper.convertDateToISO(elem.date)<= Helper.convertDateToISO(endDate))
            )
        else if(startDate !== "" && endDate !== "")
            this.setState(
                DataFiltered: this.state.Data.filter(elem => Helper.convertDateToISO(elem.date)>= Helper.convertDateToISO(startDate) && Helper.convertDateToISO(elem.date)<= Helper.convertDateToISO(endDate))
            )
        else if(startDate === "" && endDate === "")
            this.setState(
                DataFiltered: this.state.Data
            )
        
    

孩子

<TouchableWithoutFeedback onPress=async () => 
                await stateSetter(
                    ["startDate"]: "",
                    ["endDate"]: "",
                    ["startDateText"]: "From...",
                    ["endDateText"]: "To..."
                )
                dateFilter()
             >

现在我相信 Async Await 在 await 等待承诺时才起作用.....对吗? 所以按照这个逻辑,在上述代码中,await 和 async 应该对程序没有影响。

完全相反

当我不使用异步等待时,它不起作用。它仅在我使用异步等待时才有效。

当我刚刚学习异步时,我尝试了几件事,但都没有奏效。

this.setState( ...data , () => 
  this.dateFilter()
)
//Had no effect

另外:承诺没有效果。

TlDr:为什么我的 Async Await 工作正常?

【问题讨论】:

“当我不使用异步等待时,它不起作用。” - 什么不起作用?我无法真正理解你的问题。顺便说一句,如果您在非承诺值前面使用await,这就像用Promise.resolve 包装该值:await 123 -----> await Promise.resolve(123) 我必须查看规范才能确认,但我相信await 总是会以某种方式将函数其余部分的评估推迟到事件循环的下一个滴答声,到那时状态已更新。然而,我认为这是糟糕的设计,因为代码或多或少取决于这种隐藏的行为。您的最后一个示例应该看起来应该有效,并且也是我推荐的方式。 @FelixKling 我不太了解 OP 的问题,但在阅读了您的评论后,我相信 OP 会询问为什么 dateFilter 函数在等待 stateSetter 呼叫时看到更新状态?!如果是这样,即使使用awaitdataFilter 函数也不应该看到更新的状态,因为状态在特定渲染中是恒定的。 @Yousaf:这是一个类组件。使用await 将安排this.dataFilter() 在下一个tick 中被调用,并且状态可能在下一个microtick 中更新,即在dataFilter 被调用之前。 @FelixKling 啊,对。完全错过了this.setState的使用。 【参考方案1】:

tl;dr:因为您使用的是await,所以该函数的其余部分计划在将来执行,并且在那个时间点状态将已更新。不管你是否真的 await 承诺是无关紧要的。


我不知道 React 的内部结构,但我假设队列是 micro task 来更新状态。 Promise 回调也作为微任务处理(async/await 只是处理 Promise 的好方法)。

所以首先React添加了一个微任务来更新状态,然后await添加了一个微任务来恢复功能。

我们实际上可以做一个简单的实验,通过将回调传递给setState 来验证事件的顺序(没有 JSX,因为不知何故我无法使它成为一个工作的 sn-p):

class Component extends React.Component 
  async doStuff() 
    await this.setState(newState: true, () => console.log('inside setState callback', JSON.stringify(this.state)));
    console.log('after await this.setState', JSON.stringify(this.state));
  
  
  render() 
    return React.createElement('button', onClick: () => this.doStuff(), 'Click me');
  


ReactDOM.render(
  React.createElement(Component),
  document.body
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>

如果你运行它,你会看到回调首先被调用。我们知道此时状态已经更新,因为这就是回调的全部目的:它在状态更新时执行。

然后函数的其余部分被执行,由于状态已经更新,它将访问新的状态。

但是,由于您想知道为什么这样做有效,我建议您不要这样做,而只需使用 setState 回调。

【讨论】:

【参考方案2】:
    await ins 您的示例将无法按预期工作,因为 stateSetter 未返回承诺

要使等待适用于 stateSetter,它必须返回一个 Promise

stateSetter = (data) => 
   return new Promise(resolve => setTimeout(resolve, 3000));

    看看这个很好的explanation 了解这里发生了什么

【讨论】:

“不会按预期工作” 但是 OP 就是这么说的。您链接到的答案确认使用await,无论是否有承诺,都会延迟函数其余部分的执行。

以上是关于此异步等待不应该工作。为啥它会起作用?的主要内容,如果未能解决你的问题,请参考以下文章

无需等待的并发代码

dottrace 异步等待支持不起作用

添加到相同的php页面并等待用户输入

为啥异步等待操作仍然很耗时

异步/等待不等待返回值

为啥我不能将异步代码作为同步运行 [重复]