Shawn Matthews
Shawn Matthews

Reputation: 1832

Async/Await preventing second promise from executing

Lets say I have a login function

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));
  }; 

I then want to call the function onSubmit in React like follows. If the function returns an error for any reason. How do I prevent the next function in this case api.login() from being run?

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

Does a try/catch make sense in this case? I've tried several options including an inline try catch and the function runs no matter what happens as soon as the promise from this.login(); returns either an result or error.

Upvotes: 0

Views: 98

Answers (3)

Estus Flask
Estus Flask

Reputation: 222354

This is the problem that is mentioned in the answer to the previous question,

The problem with login is that its control flow is flawed. It isn't be able to efficiently catch errors because it suppresses them.

.catch(error => console.log('error:', error)) suppresses the error, while it shouldn't for proper control flow. Rejections should be handled at top level where a promise is consumed. Even if an error needs to be handled in catch (it's not necessary that console.log is needed at all), it should be rethrown.

Consistent handling of asynchronous errors is a separate concern in React. Asynchronous errors need to be caught and rethrown synchronously in lifecycle hooks (likely 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>
    ...
  }

The error that is rethrown in componentDidUpdate will propagate up to error boundary component or will result in exception, a demo.

Some additional helpers may be introduced to DRY try {...} catch (err) { this.setState({ error: err }) } boilerplate up.

Upvotes: 2

Sergio Alen
Sergio Alen

Reputation: 724

Why not put api.login() inside the first login promise then callback?

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));

};

Otherwise you can make login() to return a boolean or a truthy/falsy value then do something like this (untested code):

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

Upvotes: 1

David Gonzalez
David Gonzalez

Reputation: 146

I think this might be happening because you're just running a console.log in your catch method, instead of throwing an error or rejecting a Promise. So the try/catch block for your await continues to run as if all is OK. Try throwing an error with Promise.reject or 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"}

Upvotes: 1

Related Questions