Morgana
Morgana

Reputation: 337

Problem with promises and asynchronous operations

I have to make an HTTP POST request to the server, wait for the server response and then make another request (which will send some data of the server response back to the server). I thought it was an easy task and I did something like this:

 const promise = new Promise((resolve, reject) => {
        resolve(this.updatePU(name, 'text', selectedOption.code));
      })
      promise.then(() => {
        console.log('Calling checkExpress function');
        let express = '';
        const puOrderNo = this.props.puOrderNo;
        fetch('/CheckExpress', {
          crossDomain: true,
          method: 'POST',
          headers: {
            'Accept': 'text/xml',
            'Content-Type': 'application/json',
          },
            body: JSON.stringify({"puOrderNo": puOrderNo })
        })
        .then(response => response.text())
        .then(str => {  
            express = convert.xml2json(str);
        }).then(() => {
            const data = JSON.parse(express);
            const checkExpress = data['elements'][0].elements[0].elements[0].elements[0].elements[0].text;
            console.log('checkExpress:', checkExpress);
            if(checkExpress === 'true'){
              this.props.updatePackageTypeField(true)
            } else {
              this.props.updatePackageTypeField(false);
            }
        })
        .catch(err => console.log(err));
      })

The updatePU function is also an asynchronous function:

 updatePU = (name, type, value) => {
    const PUOrderNo = this.props.puOrderNo;
    const updatedValue = type === 'text' ? (`'${name}': '${value}'`) : (`'${name}': ${value}`);

    fetch('/ModifyPUOrder', {
      crossDomain: true,
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
        body: JSON.stringify({
          updatedValue: updatedValue,
          puOrderNo: PUOrderNo
        }),
    })
    .then(response => {
      if(response.ok){
        return response.json();
      } else {console.log(response)}
      throw new Error('Request failed!');
    }, networkError => {
      console.log(networkError.message);
    })
    .then(data => {
      if ("error" in data) {
        alert(data.error.message);
        this.refresh();
      }
      this.props.updatePUOrderForm(data);
    });
   }

The result is that promise is ignored (I think) and the second request is made before the first one! I get that the problem is that the function that is resolved in promise is an asynchronous function as well but I am not sure what to do.

Any help would be much appreciated!

Upvotes: 1

Views: 46

Answers (1)

T.J. Crowder
T.J. Crowder

Reputation: 1074028

The primary problem is that updatePU doesn't return a promise. You should return the result of the promise chain by adding return in front of fetch:

return fetch('/ModifyPUOrder', {

Then in your code at the top, don't create a new promise, use the one from updatePU:

this.updatePU(name, 'text', selectedOption.code)
.then(() => {
    console.log('Calling checkExpress function');
    // ...

There's a second (largely unrelated) problem: You're converting network error (rejection) to a fulfillment:

.then(response => {
  if(response.ok){
    return response.json();
  } else {console.log(response)}
  throw new Error('Request failed!');
}, networkError => {                 // ***
  console.log(networkError.message); // *** Converts rejection to fulfillment with `undefined`
})                                   // ***

Remember that every handler in the chain transforms what passes through it. A rejection handler that doesn't throw or return a promise that rejects converts rejection to fulfillment.

Rather than adding console.log to dump errors everywhere, just propagate the chain and, at the top level where you can't propagate the chain any further, just add a final .catch that reports/handles the error (which you do have in your first code block).

See *** comments for notes on that and a couple of other things:

updatePU = (name, type, value) => {
    const PUOrderNo = this.props.puOrderNo;
    const updatedValue = type === 'text' ? (`'${name}': '${value}'`) : (`'${name}': ${value}`);

    return fetch('/ModifyPUOrder', {
      crossDomain: true,
      method: 'POST',
      headers: {
        'Accept': 'application/json',
        'Content-Type': 'application/json',
      },
        body: JSON.stringify({
          updatedValue: updatedValue,
          puOrderNo: PUOrderNo
        }),
    })
    .then(response => {
      if(response.ok){
        return response.json();
      } // *** No console.log here
      throw new Error('Request failed!'); // *** I wouldn't hide what happened, perhaps: `throw new Error("HTTP error " + response.status);`
    }/* ***No rejection handler here */)
    .then(data => {
      if ("error" in data) {
        alert(data.error.message);
        this.refresh();
        // *** You presumably want to return here or use `else` so you don't update the form below...?
      }
      this.props.updatePUOrderForm(data);
    });
}

Upvotes: 2

Related Questions