XML file wont parse - synchronous XMLHttpRequest depreciated

Viewed 73

My HTML, JavaScript, and XML accounting system no longer fully works in Firefox, but does work in Google Chrome. Inspecting the page in Firefox, I see the following error message under the Console tab:

Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user’s experience. For more help http://xhr.spec.whatwg.org/

Looks to me that synchronous use of XMLHttpRequest has been deprecated, because it sometimes freezes the page while waiting for a response. The only time I used XMLHttpRequest in my JavaScript program is when parsing the XML file. I am parsing the XML file exactly the same way as presently outlined in the W3School XML Tutorial found here:

https://www.w3schools.com/xml/tryit.asp?filename=tryxml_parsertest

The tutorial does uses Synchronous XMLHttpRequest, as set by 'false' in the line xmlhttp.open("GET", "note.xml", false);. My notes say that asynchronized loading is off to prevent the script from running before the document is fully loaded. I tried to change 'false' to 'true', but that doesn't work either.

I am not entirely sure if this is my problem because the W3C Tutorial example still works in Firefox. However the Firefox Console does show the same 'synchronous use of XMLHttpRequest has been deprecated' warning. It also shows a bunch of other warnings.

Is my understanding of the problem correct?

If so, can you please show how the W3School XML parsing code should now be written? I pasted a copy of the original tutorial code below for convenience:

<!DOCTYPE html>
<html>
<body>
<h1>W3Schools Internal Note</h1>
<div>
<b>To:</b> <span id="to"></span><br>
<b>From:</b> <span id="from"></span><br>
<b>Message:</b> <span id="message"></span>
</div>

<script>
var xmlhttp, xmlDoc;
xmlhttp = new XMLHttpRequest();
xmlhttp.open("GET", "note.xml", false);
xmlhttp.send();
xmlDoc = xmlhttp.responseXML;
document.getElementById("to").innerHTML=
xmlDoc.getElementsByTagName("to")[0].childNodes[0].nodeValue;
document.getElementById("from").innerHTML=
xmlDoc.getElementsByTagName("from")[0].childNodes[0].nodeValue;
document.getElementById("message").innerHTML=
xmlDoc.getElementsByTagName("body")[0].childNodes[0].nodeValue;
</script>

The first 5 lines of the script deal with parsing the XML file, the rest of the script inserts the information from the XML file into the webpage.

Thank-you kindly for any help!!!

1 Answers

The warning regarding synchronous XMLHttpRequest is just that, a warning. Testing locally, the request is still performed and properly parsed under Firefox, Chrome and Safari. However, it's not clear what you mean by "no longer fully works", so perhaps you could elaborate on what exactly is/isn't working in this case.

Looks to me that synchronous use of XMLHttpRequest has been deprecated, because it sometimes freezes the page while waiting for a response.

Actually, what you are likely experiencing there is an example of the "detrimental effects" mentioned in your warning message.

My notes say that asynchronized loading is off to prevent the script from running before the document is fully loaded.

The more appropriate way to do this is to add a listener to the load event fired by Window, however, it will still be best to also perform the request asynchronously. Below is a modified version of your posted code, with:

  • execution waiting on the load event
  • performing the request asynchronously
  • testing the response to ensure it is valid
// 1) Wait for the Window `load` event to be fired
window.addEventListener('load', () => {
  const xmlhttp = new XMLHttpRequest();
  xmlhttp.open('GET', 'note.xml');
  // 2) Wait for the XMLHttpRequest `load` event to be fired
  xmlhttp.addEventListener('load', ({ target }) => {
    // 3) Ensure that the response is valid
    if (target.status < 200 || 300 <= target.status) {
      console.error(`Received status "${target.status}"`);
      return;
    }

    const contentType = target.getResponseHeader('Content-Type');
    if (contentType !== 'application/xml') {
      console.error(`Received Content-Type "${contentType}"`);
      return;
    }

    const xmlDoc = target.responseXML;
    if (xmlDoc === null) {
      console.error('Unable to parse XML response');
      return;
    }

    document.getElementById('to').innerHTML = xmlDoc.getElementsByTagName('to')[0].childNodes[0].nodeValue;
    document.getElementById('from').innerHTML = xmlDoc.getElementsByTagName('from')[0].childNodes[0].nodeValue;
    document.getElementById('message').innerHTML = xmlDoc.getElementsByTagName('body')[0].childNodes[0].nodeValue;
  });
  xmlhttp.addEventListener('error', error => {
    console.error(`Received error "${error}"`);
  });
  xmlhttp.send();
});

As for the XML parsing, my understanding is that the parsing to Document using the responseXML property is orthogonal to whether the request is performed synchronously or asynchronously. If you want to handle the parsing explicitly, you can replace the use of responseXML with the following use of DOMParser and parseFromString:

const parser = new DOMParser();
const xmlDoc = parser.parseFromString(target.responseText, contentType);
if (xmlDoc.querySelector('parsererror') !== null) {
  console.error('Unable to parse XML response');
  return;
}

Finally, if you've come this far, consider moving over to using the Fetch API which is a more modern, more ergonomic way of performing these asynchronous requests in the browser, using Promises and thus open to use of async/await to clean up the flow. Using that, the code could become:

window.addEventListener('load', async () => {
  const request = new Request('note.xml');
  try {
    const response = await fetch(request);
    if (response.ok === false) {
      console.error('Response was not ok');
      return;
    }

    const contentType = response.headers.get('Content-Type');
    if (contentType !== 'application/xml') {
      console.error(`Received Content-Type "${contentType}"`);
      return;
    }

    const parser = new DOMParser();
    const xmlDoc = parser.parseFromString(await response.text(), contentType);
    if (xmlDoc.querySelector('parsererror') !== null) {
      console.error('Unable to parse XML response');
      return;
    }

    document.getElementById('to').innerHTML = xmlDoc.getElementsByTagName('to')[0].childNodes[0].nodeValue;
    document.getElementById('from').innerHTML = xmlDoc.getElementsByTagName('from')[0].childNodes[0].nodeValue;
    document.getElementById('message').innerHTML = xmlDoc.getElementsByTagName('body')[0].childNodes[0].nodeValue;
  } catch (error) {
    console.error(`Received error "${error}"`);
  }
});
Related