【问题标题】:Async Await is not working as expected: ReactJS异步等待未按预期工作:ReactJS
【发布时间】:2020-03-04 05:23:29
【问题描述】:

我有这个问题,用户必须在成功登录后重定向到相应的仪表板。假设用户在两个配置文件 "p1" 和 "p2" 中有帐户。登录成功后,我正在制作 fetch API 以查看用户是否有相应配置文件的条目。

1)假设用户在 p1 中有条目,我需要将他重定向到“p1”仪表板;

2) 如果在 p2 中有条目,则将他重定向到“p2”仪表板。

3) 如果 p1 或 p2 中都没有条目,则重定向到配置页面,他可以在其中创建一些条目。

4) 如果在两个配置文件中,则用户将被要求选择相应的仪表板

现在就我而言,我编写的代码没有按预期工作。即使我在“p2”中没有帐户,但在“p1”中有帐户,它也会将我带到配置页面。有人可以帮忙看看这段代码有什么问题吗?

请注意,fetch 调用工作正常!它给了我一系列的项目。如果没有项目,则返回一个空数组

// On login 
  handleLogin = async () => {
        // after successful signin
        await Promise.all([
          this.setDashboard("p1"),
          this.setDashboard("p2"),
        ]);
        this.navigateToDashboards();
  };

   setDashboard = async (profile) => {
    let response, value;
    let type = profile;
    if (type === "p1") {
      response = await this.fetchUserAccounts(type);
      value = !!response && response.length ? true : false;
      this.setState({ isP1: value });
    } else {
      response = await this.fetchUserAccounts(type);
      value = !!response && response.length ? true : false;
      this.setState({ isP2: value });
    }
  };

  fetchUserAccounts = async (type) => {
    try {
      let response = await fetch({
        url: "/fetch/entries",
        method: "POST",
        body: {
          profile: type,
        }
      );
      return response.json();
    } catch (error) {
      console.log(error);
    }
  };


    navigateToDashboards = () => {
    let { isP1, isP2 } = this.state;
    if (isP1 && isP2) {
      // CODE FOR PROMPTING WHICH DASHBOARD TO GO
    } else if (!isP1 && !isP2) {
      this.props.history.push("/configpage"); 
    } else if (isP1) {
      this.props.history.push("/p1dashboard");
    } else if (isP2) {
      this.props.history.push("/p2dashboard");
    }
  };

【问题讨论】:

  • 你需要传递一个对象来获取。您所拥有的应该是语法错误。它应该是 fetch({ url: ..., })。注意大括号
  • 前提似乎是fetch()如果服务器返回404会抛出错误。它没有,afaik。
  • @Malvolio 问题与 fetch 无关。我刚刚给出了一个整体的功能。获取工作正常。已更新问题
  • setState (通常)是异步的。在一个反应​​组件中,你将作用于render。那时您可以确定this.state 已更新。

标签: javascript reactjs ecmascript-6 async-await react-router


【解决方案1】:

您在上面编写的代码存在一些问题,它们可能比我们在 sn-p 中看到的更深——为什么在客户端而不是从服务器发送仪表板类型的逻辑?

还有cloudType没有在setDashboard方法中指定。

我不知道你想要达到什么目的,所以我猜 - 你应该在 componentDidMount 中使用 fetchUserAccounts 并将响应保存在状态中。

我想出了这样的东西,代码中有一个TODO:

class X extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      p1: null,
      p2: null,
    };
  }

  async componentDidMount() {
    const [p1, p2] = await Promise.all([
      this.fetchUserAccounts('p1'),
      this.fetchUserAccounts('p1'),
    ]);
    // NOTE: setState has a second parameter, which is a callback – this way you make sure that the state has been updated – read more here:
    // https://reactjs.org/docs/react-component.html#setstate
    this.setState(s => ({ ...s, p1, p2 }), this.navigateToDashboards);
  }

  fetchUserAccounts = async (type) => {
    try {
      let response = await fetch({
        url: "/fetch/entries",
        method: "POST",
        body: {
          profile: type,
        },
      });
      return response.json();
    } catch (error) {
      console.log(error);
    }
  };

  navigateToDashboards = () => {
    const { history } = this.props;
    const { p1, p2 } = this.state;
    // TODO: Implement your logic how you decide 
    if (isP1 && isP2) {
      // CODE FOR PROMPTING WHICH DASHBOARD TO GO
      return;
    }
    if (isP1) return history.push('/p1dashboard');
    if (isP2) return history.push('/p2dashboard');
    return history.push('/configpage');
  }
}

如果这不起作用,至少看看代码结构。有些地方不需要else 语句。使用setState 的函数版本。利用对象/数组解构。您可能还应该根据 !!response && response.length ? true : false; 行了解 JS 世界中的 falsetrue 是什么——或者我在这里遗漏了一些东西。

【讨论】:

    【解决方案2】:

    问题是你用错了async / await

    await 处理承诺——你给它一个承诺,它会等待它解决/拒绝。如果你不给它一个承诺 - 就像传递一个立即解决的承诺一样。

    你的 setDashboard 函数没有返回一个承诺,所以发生的事情是这段代码:

    await Promise.all([
              this.setDashboard("p1"),
              this.setDashboard("p2"),
            ]);
    

    实际上会立即解决,但 setDashboard 函数直到 this.navigateToDashboards(); 才会执行。为什么?因为它们被定义为异步,这将它们置于事件循环中。然后是await,但它会立即解析,因为里面的函数没有返回一个promise(嗯,它确实,因为它是异步的,但就像在第一行写return promise.resolve()一样 - 所以await确实仅将函数置于事件循环中)- 代码继续以同步方式执行,直到到达函数末尾(例如,执行this.navigateToDashboards();,然后才进入事件循环并执行setDashboards 函数。

    在同一主题上,response = await this.fetchUserAccounts(type); 行也有多余的等待,因为fetchUserAccounts() 不返回承诺。

    要正确使用 async / await- 确保您的 async 函数返回一个承诺,例如:

    setDashboard = async (profile) => {
     return new Promise( resolve, reject => {
        let response, value;
        let type = profile;
        if (type === "p1") {
          response = await this.fetchUserAccounts(type);
          value = !!response && response.length ? true : false;
          this.setState({ isP1: value });
          resolve();
        } else {
          response = await this.fetchUserAccounts(cloudType);
          value = !!response && response.length ? true : false;
          this.setState({ isP2: value });
          resolve();
        }
      })
    };
    

    至于 fetchUserAccount:

    fetchUserAccounts = async (type) => {
      return new Promise( resolve, reject => {
        try {
          let response = await fetch({
            url: "/fetch/entries",
            method: "POST",
            body: {
              profile: type,
            }
          );
          resolve(response.json());
        } catch (error) {
          console.log(error);
          reject(error);
        }
      }
    };
    

    请注意,您现在必须使用 .catch 处理 Promise 拒绝,否则您将收到 unhandled promise rejection error

    【讨论】:

    • 这里有些问题。例如,setDashboard() 被声明为async,因此它总是自动返回一个承诺。您将其额外包装在一个承诺中是完全没有必要的,并且被认为是一种反模式。 fetchUserAccounts() 也一样。这个答案大部分都是错误的。
    • @jfriend00 它返回一个承诺,就像我写的那样,但由于没有“return”语句,它会自动解决承诺,你无法控制它何时会。如果fetchUserAccounts 有一个回报——但只是部分回报,所以同样的事情也会发生在那里。这也是有道理的,因为它会将他重定向到配置页面,就好像在状态到达fetchUserAccounts 之前没有更新配置文件类型一样。如果您的异步函数没有返回任何承诺或根本没有返回任何东西 - 它会立即解决,因为 await 没有什么可以等待的。
    • 因为async 函数将在函数中的所有await 语句完成之后解析它的承诺,然后在函数完成后执行最后一个await 之后的代码。由于没有return 语句,因此promise 的resolved 值将是undefined,但promise 仍会在适当的时机正确解析。我并不是说 OP 的代码是完美的,但你对需要做什么的描述是错误的。您无需从 async 函数手动返回承诺。
    猜你喜欢
    • 2019-04-19
    • 2019-04-07
    • 2022-01-22
    • 2018-12-02
    • 1970-01-01
    • 2021-07-15
    • 2021-12-08
    • 2023-03-03
    • 2019-08-31
    相关资源
    最近更新 更多