【问题标题】:Async/Await preventing second promise from executing异步/等待防止第二个承诺执行
【发布时间】:2018-09-13 00:03:42
【问题描述】:

假设我有一个登录功能

login = () => {
    let url = baseURL + '/user/login?_format=json';  

    let data = {
      "name": this.state.email,  
      "pass": this.state.password
    };


    return axios({
      url,
      method: "POST",
      headers: {
        'Accept':  'application/json',
        'Content-Type': 'application/json',
      },
      withCredentials: true,
      credentials: 'same-origin'
      })
      .then(function(result) {
        console.log('result:', result);
        this.setState({csrfToken: result.data.csrf_token}); 
        this.setState({logoutToken: result.data.logout_token});
        return result;
      })
      .catch(error => console.log('error:', error));
  }; 

然后我想在 React 中调用 onSubmit 函数,如下所示。如果函数因任何原因返回错误。在这种情况下如何防止下一个函数 api.login() 运行?

{api => (
            <Form
                onSubmit={async e => {
                  e.preventDefault();
                  await this.login();
                  api.login()
                }}
              >
    <input/>
    </Form>

在这种情况下,try/catch 有意义吗?我已经尝试了几个选项,包括内联 try catch,只要来自this.login(); 的承诺返回结果或错误,无论发生什么,该函数都会运行。

【问题讨论】:

    标签: reactjs promise async-await try-catch


    【解决方案1】:

    这是the answer to the previous question中提到的问题,

    登录的问题在于它的控制流程有缺陷。它无法有效地捕获错误,因为它会抑制它们。

    .catch(error =&gt; console.log('error:', error)) 抑制错误,但它不应该用于正确的控制流。拒绝应该在使用承诺的顶层处理。即使需要在catch 中处理错误(根本不需要console.log),也应该重新抛出它。

    异步错误的一致处理是 React 中的一个单独问题。需要在生命周期钩子中同步捕获和重新抛出异步错误(可能是componentDidUpdate):

      componentDidUpdate() {
        if (this.state && this.state.error) {
          throw this.state.error;
        }
      }
    
      onSubmit = async e => {
        try {
          e.preventDefault();
          await this.login();
          api.login();
        } catch (err) {
          this.setState({ error: err });
        }
      }
    
      render() {
        ...
        <Form onSubmit={this.onSubmit}>
          <input/>
        </Form>
        ...
      }
    

    componentDidUpdate 中重新抛出的错误将传播到错误边界组件或将导致异常,demo

    DRY try {...} catch (err) { this.setState({ error: err }) } 样板文件中可能会引入一些额外的助手。

    【讨论】:

      【解决方案2】:

      为什么不将api.login() 放在第一个登录承诺then 回调中?

      login = () => {
      let url = baseURL + '/user/login?_format=json';  
      
      let data = {
        "name": this.state.email,  
        "pass": this.state.password
      };
      
      
      return axios({
        url,
        method: "POST",
        headers: {
          'Accept':  'application/json',
          'Content-Type': 'application/json',
        },
        withCredentials: true,
        credentials: 'same-origin'
        })
        .then(function(result) {
          console.log('result:', result);
          this.setState({csrfToken: result.data.csrf_token}); 
          this.setState({logoutToken: result.data.logout_token});
          api.login() // <----- if you want to check result just wrap it in an if statement if (result) api.login()
          return result;
        })
        .catch(error => console.log('error:', error));
      

      };

      否则,您可以让 login() 返回布尔值或真值/假值,然后执行以下操作(未经测试的代码):

      {api => (
              <Form
                  onSubmit={async e => {
                    e.preventDefault();
                    await this.login() && api.login()  
                  }}
                >
      <input/>
      </Form>
      

      【讨论】:

      • 你有第一个建议的例子吗?
      • 你未经测试的代码也能完美运行......我只是好奇
      • 为什么不将 api.login() 放入第一个登录承诺然后回调? 我想这是因为 apilogin 范围内不可用,而这些两个功能解耦。
      • 是的,但这是一个鼓励重构选项思考过程的问题
      【解决方案3】:

      我认为这可能会发生,因为您只是在 catch 方法中运行 console.log,而不是抛出错误或拒绝 Promise。因此,您的 await 的 try/catch 块会继续运行,就好像一切正​​常。尝试使用 Promise.reject 或 new Error() 抛出错误。

      var catchWithoutRejection = async () => {
        await console.log('hello')
        console.log('running')
      }
      
      catchWithoutRejection();
      
      // hello
      // running
      // Promise {<resolved>: undefined}
      
      var catchWithRejection = async () => {
        await Promise.reject("hello")
        console.log('not running')
      }
      
      catchWithRejection();
      // Promise {<rejected>: "hello"}
      

      【讨论】:

        猜你喜欢
        • 2017-06-15
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2018-02-03
        • 2018-03-05
        • 1970-01-01
        • 2020-03-26
        • 2018-12-24
        相关资源
        最近更新 更多