-2

I have a single page web app. Some of its behaviour relies on modifying a section of the page and then call some functions to format this section AFTER it has been modified (MathJax being one of the most important post modification calls I need to do).

Since Ajax is asynchronous, some of the time I didn't get the desired effect as I would get a non-formatted page, since the scripts would run before the contents of the page were ready, to fix this I started to call XMLHttpRequest().open() with false to make it synchronous and ensure that the page contents were ready before calling the scripts.

This however makes the page throw a warning

"Synchronous XMLHttpRequest on the main thread is deprecated because of its detrimental effects to the end user’s experience."

despite behaviour being exactly what I need. I don't like relying on deprecated behaviour because it may work now, but 2 months down the road if they change things and break it, I'd have to reformat my entire code and solve the problem again in a different way.

How concerned should I be about this warning, and if it's likely to be broken in future updates of the web, what should I do instead?

EDIT: JS code:

function changeSection(section, page, button, sync=true)
{
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      $(section).empty();
      $(section).append(this.responseText);

      clearButtons();
      setButton(button);
    }
  };
  xhttp.open("GET", page, sync);
  xhttp.send();
}

function setLagrange() {
  path = MathLog_path + "modelling/lagrange_interpolation.html";

  changeSection("#VolatileSection", path, "lagrange_button", false);

  $('pre code').each(function(i, block) {
    hljs.highlightBlock(block);
  });
  MathJax.Hub.Config({
    tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}
  });
  MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
}
Makogan
  • 8,208
  • 7
  • 44
  • 112
  • Just asynchronously call the necessary functions after the modifications. Not really possible to further help you without seeing actual code. – Dexygen Nov 04 '18 at 18:21
  • I cannot call the asynchronous calls after the modifications, the asynchronous calls ARE the modifications – Makogan Nov 04 '18 at 18:24
  • 2
    Possible duplicate of [JavaScript console.log causes error: "Synchronous XMLHttpRequest on the main thread is deprecated..."](https://stackoverflow.com/questions/24639335/javascript-console-log-causes-error-synchronous-xmlhttprequest-on-the-main-thr) or [jQuery has deprecated synchronous XMLHTTPRequest](https://stackoverflow.com/questions/27736186/jquery-has-deprecated-synchronous-xmlhttprequest/27736383#27736383) – Andrew Marshall Nov 04 '18 at 18:26
  • @Makogan try (if you are using jQuery) `$(document).ready(setLagrange);` If you're modifying existing code it's not worth it to add new complexities like async calls, but for new code you should look up promises in javascript.. – Dominique Fortin Nov 04 '18 at 18:50

1 Answers1

4

How concerned should I be about this warning,

Reasonably. :-) It's telling you this is poor practice for a reason: It impacts the user experience negatively.

...what should I do instead?

Just keep using asynchronous ajax, and call the functions that need to be called when it's finished from the completion handler.

If using XHR:

const xhr = new XMLHttpRequest();
xhr.addEventListener("load", () => {
    // Call your functions here
});
xhr.addEventListener("error", () => {
    // Handle errors here
});
xhr.open("GET", "/path/to/the/resource");
xhr.send();

...but I'd use fetch:

fetch("/path/to/the/resource")
.then(response => {
    if (!response.ok) {
        throw new Error("HTTP error " + response.status);
    }
    return response.appropriateMethodToReadBodyHere();
})
.then(data => {
    // Call your functions here
})
.catch(error => {
    // Handle errors here
});

...perhaps even in an async function:

try {
    const response = await fetch("/path/to/the/resource");
    if (!response.ok) {
        throw new Error("HTTP error " + response.status);
    }
    const data = await response.appropriateMethodToReadBodyHere();
    // Call your functions here
} catch (error) {
    // Handle errors here
}

You added some code. Here's a minimal-changes example:

function changeSection(section, page, button, sync=true, callback = () => {})
// *** ------------------------------------------------^^^^^^^^^^^^^^^^^^^^^
{
  var xhttp = new XMLHttpRequest();
  xhttp.onreadystatechange = function() {
    if (this.readyState == 4 && this.status == 200) {
      $(section).empty();
      $(section).append(this.responseText);

      clearButtons();
      setButton(button);
      callback(); // ***
    }
  };
  xhttp.open("GET", page, sync);
  xhttp.send();
}

function setLagrange() {
  path = MathLog_path + "modelling/lagrange_interpolation.html";

  changeSection("#VolatileSection", path, "lagrange_button", false, () => {
  // *** ---------------------------------------------------------^^^^^^^^^
      $('pre code').each(function(i, block) {
        hljs.highlightBlock(block);
      });
      MathJax.Hub.Config({
        tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}
      });
      MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
  });
//^^^ ***
}

...but as you're using ES2015+ features, I'd use fetch and return a promise from changeSection:

function changeSection(section, page, button, sync=true)
{
  return fetch(page)
    .then(response => {
      if (!response.ok) {
          throw new Error("HTTP error " + response.status);
      }
      return response.text(); // or .json() or whatever
    })
    .then(() => {
      $(section).empty();
      $(section).append(this.responseText);

      clearButtons();
      setButton(button);
    });
}

function setLagrange() {
  path = MathLog_path + "modelling/lagrange_interpolation.html";

  changeSection("#VolatileSection", path, "lagrange_button", false).then(() => {
  // *** ----------------------------------------------------------^^^^^^
      $('pre code').each(function(i, block) {
        hljs.highlightBlock(block);
      });
      MathJax.Hub.Config({
        tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}
      });
      MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
  });
}

or using async functions:

async function changeSection(section, page, button, sync=true)
{
  const response = await fetch(page);
  if (!response.ok) {
      throw new Error("HTTP error " + response.status);
  }
  await response.text(); // or .json() or whatever
  $(section).empty();
  $(section).append(this.responseText);

  clearButtons();
  setButton(button);
}

async function setLagrange() {
  path = MathLog_path + "modelling/lagrange_interpolation.html";

  await changeSection("#VolatileSection", path, "lagrange_button", false);
  $('pre code').each(function(i, block) {
    hljs.highlightBlock(block);
  });
  MathJax.Hub.Config({
    tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]}
  });
  MathJax.Hub.Queue(["Typeset",MathJax.Hub]);
}

Note that setLagrange must be called from an async function, or its promise must be consumed explicitly:

setLagrange(/*...*/)
.catch(error => {
    // Handle error
});
T.J. Crowder
  • 1,031,962
  • 187
  • 1,923
  • 1,875