0

I'm struggling with dealing with my fetch() POST request. It's working successfully, and I can see the data just fine and work with it - but only within the fetch() call. I want to pass data back to App.js (I keep my fetch() API in its own utility module .js file)... but the timing is off. Based on console log debugging, it looks like the function that contains the fetch is returning to the original call before the fetch fully resolves.

These are the console results. The success/text object is what I return from the N4RecordScan.submit() function which contains my fetch(). Then a few lines later, we see the promise resolve. So my App.js is left hanging with no data.

I'd appreciate any guidance!! I feel like I'm close!

{success: "", text: ""}  
Processing final response.  
Fetch finished loading: OPTIONS 
{argo:gateresponse: {…}, status: "0", statusid: "OK"}

Here'sa snippet from my App.JS which calls and processes the fetch function further down.

  processResponse(myResponseObject) {
    this.setState({
      responseAlerts: this.state.responseAlerts.push(myResponseObject)
    });
    console.log('Processing final response. '+ myResponseObject.success + ' ' + myResponseObject.text);
  }

  sendRequest() {
    let response = N4RecordScan.submit(this.interpolateRequest(), this.state.server, this.state.endpoint);
    this.processResponse(response);
  }

Here's the function where my fetch() resides:

export const N4RecordScan = {

    submit(data, server, endpoint) {
        let headers = new Headers();
        let success = '';
        let text = '';

        headers.append('Content-Type', 'text/xml');
        headers.append('SOAPAction', 'basicInvoke');
        headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));

        let dataPrefix = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:arg="http://www.navis.com/services/argobasicservice"><soapenv:Header/><soapenv:Body><arg:basicInvoke><arg:scopeCoordinateIds>APMT/USLAX/LAX/LAX</arg:scopeCoordinateIds><arg:xmlDoc><![CDATA[';
        let dataSuffix = ']]></arg:xmlDoc></arg:basicInvoke></soapenv:Body></soapenv:Envelope>';

        data = dataPrefix + data + dataSuffix;

        console.log('about to send ' + data);

        fetch(server + endpoint, {
            body: data,
            method: 'POST',
            mode: 'cors',
            headers: headers,
            credentials: 'include'
        })
            .then(function(response){
                return response.text();



             /*   if (response.status === 200 || response.status === 0) {
                    // Success!
                    console.log('Success: ' + response.text());
                    return {
                        success: true,
                        text: response.text()
                    };
                } else {
                    // Failure!
                    console.log('Fail: ' + response.statusText);
                    return {
                        success: false,
                        text: response.statusText
                    };

                } */
            } )
            .then(function(rspText){
                // The raw response contains decoded HTML tags... we need to clean that up.

                // Remove dashes from the xml responses... the eventual js object wont like them
                rspText = rspText.replace(/-/g, "");
                // Convert the text response to XML
                var parser = new DOMParser;
                var dom = parser.parseFromString(
                    rspText,
                    'text/html');
                var decodedString = dom.body.textContent;

                // use the DOMParser browser API to convert text to a Document
                var XML = new DOMParser().parseFromString(decodedString, "text/xml");
                // and then use #parse to convert it to a JS object
                var responseXmlObject = parse(XML);
                console.log(responseXmlObject);
                success = true;
                text = responseXmlObject.messages;
                alert(responseXmlObject.messages.messagedetail);
            })
            .catch(function(error) {
                // Networking Failure!
                console.log('NetworkFail: ' + error);
                                success = false;
                text = error;

            });
        //.done();

        console.log({
            success: success,
            text: text
        });
        return {
            success: success,
            text: text
        };

    }
};
runelynx
  • 403
  • 2
  • 4
  • 17
  • 1
    You're mixing the asynchronous nature of fetch with the synchronous approach of your return. You could make your submit method return the promise created by fetch, and return the success object in your last `then`/`catch` clause. – E. Sundin Feb 25 '18 at 01:51
  • @E.Sundin Thanks; to return a promise created by a fetch... is that basically: return fetch(...) ? Assuming I follow that - how do I end up consuming my success object --- do I need to transact something on the var that holds the promise, or does the return in the final then/catch deliver my success object automatically to wherever I return the promise? I hope that makes sense, this gets confusing :( – runelynx Feb 25 '18 at 01:55

1 Answers1

1

The problem is you are mixing async and sync operations, you should be doing

processResponse(myResponseObject) {
  this.setState({
    responseAlerts: this.state.responseAlerts.push(myResponseObject)
  });
  console.log('Processing final response. '+ myResponseObject.success + ' ' + myResponseObject.text);
}

sendRequest() {
  N4RecordScan.submit(this.interpolateRequest(), this.state.server, this.state.endpoint)
   .then(function (response){
      this.processResponse(response);
   })
}

,

export const N4RecordScan = {

  submit(data, server, endpoint) {
      let headers = new Headers();
      let success = '';
      let text = '';

      headers.append('Content-Type', 'text/xml');
      headers.append('SOAPAction', 'basicInvoke');
      headers.append('Authorization', 'Basic ' + base64.encode(username + ":" + password));

      let dataPrefix = '<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/" xmlns:arg="http://www.navis.com/services/argobasicservice"><soapenv:Header/><soapenv:Body><arg:basicInvoke><arg:scopeCoordinateIds>APMT/USLAX/LAX/LAX</arg:scopeCoordinateIds><arg:xmlDoc><![CDATA[';
      let dataSuffix = ']]></arg:xmlDoc></arg:basicInvoke></soapenv:Body></soapenv:Envelope>';

      data = dataPrefix + data + dataSuffix;

      console.log('about to send ' + data);

      return fetch(server + endpoint, {
          body: data,
          method: 'POST',
          mode: 'cors',
          headers: headers,
          credentials: 'include'
      })
          .then(function(response){
              return response.text();



           /*   if (response.status === 200 || response.status === 0) {
                  // Success!
                  console.log('Success: ' + response.text());
                  return {
                      success: true,
                      text: response.text()
                  };
              } else {
                  // Failure!
                  console.log('Fail: ' + response.statusText);
                  return {
                      success: false,
                      text: response.statusText
                  };

              } */
          } )
          .then(function(rspText){
              // The raw response contains decoded HTML tags... we need to clean that up.

              // Remove dashes from the xml responses... the eventual js object wont like them
              rspText = rspText.replace(/-/g, "");
              // Convert the text response to XML
              var parser = new DOMParser;
              var dom = parser.parseFromString(
                  rspText,
                  'text/html');
              var decodedString = dom.body.textContent;

              // use the DOMParser browser API to convert text to a Document
              var XML = new DOMParser().parseFromString(decodedString, "text/xml");
              // and then use #parse to convert it to a JS object
              var responseXmlObject = parse(XML);
              console.log(responseXmlObject);
              success = true;
              text = responseXmlObject.messages;
              alert(responseXmlObject.messages.messagedetail);
          })
          .catch(function(error) {
              // Networking Failure!
              console.log('NetworkFail: ' + error);
                              success = false;
              text = error;

          })
          .then(function () {
            console.log({
                success: success,
                text: text
            });
            return {
                success: success,
                text: text
            };
          })
      //.done();



  }
};

You should be returning the promise from fetch inside the submit function so that the function in App.js can wait until fetch is done to do processing

Jiby Jose
  • 3,745
  • 4
  • 24
  • 32
  • Thanks! That definitely returns the object! It generates a new error on that final then() though, where this.processResponse() is called. Can it not access "this" from within the then()? ((Uncaught (in promise) TypeError: Cannot read property 'processResponse' of undefined)) – runelynx Feb 25 '18 at 02:10
  • 1
    you can assign `this` to a variable `let self = this`, and use it inside the promise then – Jiby Jose Feb 25 '18 at 02:43
  • Found it - this was the answer to that issue. Very random! https://stackoverflow.com/questions/40189424/uncaught-in-promise-typeerror-cannot-read-property-setstate-of-undefined – runelynx Feb 25 '18 at 02:54