Are there any reasons to use synchronous XHR in JavaScript?

Published on

JavaScript has 2 primary APIs for making http requests. The old XMLHttpRequest and the relatively new fetch()

Nowadays fetch() is recommended because it doesn't cause much boilerplate code. Just for comparison look at the difference.

XMLHttpRequest: // 1. Creating new XMLHttpRequest object let xhr = new XMLHttpRequest(); // 2. Preparing it: GET request by URL /article/.../load xhr.open('GET', '/article/xmlhttprequest/example/load'); // 3. Sending the request xhr.send(); // 4. This code works after getting the server response xhr.onload = function() { if (xhr.status != 200) { // checking HTTP response status, if status is not 200 then an error occurred alert(`Error ${xhr.status}: ${xhr.statusText}`); // For example, 404: Not Found } else { // if everything went smooth, printing the result alert(`Successfully received ${xhr.response.length} bytes`); // response -- this is the server response } }; fetch(): const response = await fetch('/article/xmlhttprequest/example/load'); if (response.ok) { alert(await response.text()); } else { alert(`Error ${response.status}: ${response.statusText}`); }

So much cleaner with fetch().

Limitations of fetch()

However, fetch() has some missing features, and one fundamental missing feature is the support for synchronous requests. fetch() always returns promises, so it can't work synchronously. xhr.open(), on the other hand, has the third async boolean parameter which indicates whether the request should be synchronous or asynchronous. By default it's true. So, in the example above it will run asynchronously. In order to make it work synchronously, we must write like this:

xhr.open('GET', '/article/xmlhttprequest/example/load', false);

Ok, people might ask why to use sync requests if it's obviously bad for user experience because it blocks the main thread execution, causing the site to freeze and overall disrupting the event loop. Even the browsers and the XHR standard warn about sync XHR deprecation and its possible future removal outside of workers. On top of that, in the recent years sync XHR started to throw exceptions when it's used during the page unload. So why use it at all?

Where sync is the lesser of 2 evils

Well, it's not that simple. It's true that it blocks the main thread execution and that's a bad thing, however it's sometimes the lesser of 2 evils in some situations. The problem with asynchronous code is that it's like a virus, its calling function should either be asynchronous too or they have to "slip" on the asynchronous part, which will likely to work incorrectly. Switching some piece of code to asynchronous mode (for example, once it was a regular synchronous function but later some functionality was decided to be moved to a backend, so http request is needed) can introduce problems. These are the main cases:

  1. A lot of code is spread all over the place in a huge codebase and that code has assumptions that certain things will work synchronously. For example, I worked on one project where on the client side dynamic translations were preformed. The translations were embedded in the HTML as a key - value JSON, and when a certain text snippet is needed, we called a synchronous function with the passed snippet key. That function parsed the JSON and extracted the corresponding snippet text. The problem is that it's hard to know which snippets are used dynamically on each page, and we oftentimes forgot to add some snippets. We needed an easy way to deal with this problem. Rewriting the whole thing is risky, and even if we succeeded, the implementation would be much more complicated than the current approach. So the obvious solution is to request the missing snippets via sync XHR. Making few sync requests where the user might not even notice anything at all was a better solution. Of course, we moved the critical and commonly used snippets to the JSON, so no sync requests will occur most of the time, especially during the page load.
  2. If some library functionality switches to asynchronous mode, this will break all the code that depends on it. You can't control the code that uses your library, so the only option is to keep the old synchronous functionality (at least for a while) if you care about compatibility.
  3. Sync XHR is very useful for frameworks in development mode.

There are probably other reasons in favor of sync XHR. But these reasons alone will probably make the removal of sync XHR from the main thread less likely. I hope the web standards committee understands this and the removal of this functionality will probably remain in the status quo.

On a final note

That being said, the consensus is to avoid using sync xhr as much as possible but at the same time understand that there are rare situations where it's justified.

The best developers understand that there is nothing that is absolutely right. Patterns, methodologies and best practices are guidelines, you must know when it's appropriate to follow them, but also know when you need to break them. Software development is an art and is always about tradeoffs.