Understanding XHR

Understanding XHR

Hello everyone! In this article, we are going to understand about XHR, and then compare it with other data fetching methods such as fetch.

What is XHR?

Firstly what is XHR? XHR is the acronym for XMLHttpRequest. By seeing the name, you'd wonder why we are reading about XML in Javascript.

Don't get deceived by the name, XHR can be used to receive JSON, HTML, XML as well as plain text. It is a built-in browser object which is used for communication between the client and the server. It is similar to what Fetch does. If you're unfamiliar with Fetch, no worries, you can read about it here.

XHR is used in AJAX programming. For those of you who don't know what is AJAX, AJAX is Asynchronous Javascript and XML. Basically, by this technique, the client and server can communicate asynchronously. You can read more about it here.

Why use XHR?

So you might be wondering why do we need XHR and why do we need to do asynchronous programming?

Asynchronous programming

Asynchronous programming helps make the system more efficient and prevents long time waits. For eg, there is a request which needs to fetch data from the server. This operation is time-consuming and if we use synchronous programming, the browser will wait for the data to be fetched, and only then the other tasks will be executed. This will lead to code blocking. To avoid this, we use asynchronous programming.

XHR events

The following events are widely used:

  • status - returns the HTTP status code
  • statusText - returns the HTTP status message
  • load – when the request is complete, and the response is fully downloaded
  • error – when the request isn't completed, eg. network down or invalid URL
  • progress – triggers in intervals when the response is being downloaded, informs the user how much of the response has been downloaded
  • timeout - specifies a timeout and if the event is not completed within the given time, it gets canceled and the timeout event gets triggered (in ms)
    xhr.timeout = 5000;
    
  • abort - this triggers the abort event, and xhr.status becomes 0
    xhr.abort()
    

Uses of XHR

  • Update a web page without reloading the page
  • Request data from a server (after the page has loaded)
  • Receive data from a server (after the page has loaded)
  • Send data to a server in the background, without code blocking

How to use XHR to make requests?

  1. Creating a constructor. This constructor has no arguments

    let xhr = new XMLHttpRequest();
    
  2. Right after the first step, we usually initialize it

    xhr.open(method, URL, [async, user, password])
    
    • method can either be "GET" or "POST" depending on the type of request
    • URL - from where we have to get or post the data
    • async - by default it is true, but in cases where a synchronous request has to be made, this field has to be explicitly set to 'false'
    • user, password - these are optional fields and are usually used if any auth request is made
  3. The above method doesn't call the request. It just configures it, but it is only called when we send it

    xhr.send([body])
    

    the body parameter here is optional. Usually GET requests don't require this body parameter as we are only getting the information. On the other hand, POST requests need this body parameter, this parameter contains the information which has to be posted.

  4. Getting the response

    xhr.onload = function() {
    alert("Loaded");
    };
    xhr.onerror = function() {
    // only triggers if the request couldn't be made at all
    alert("Network Error");
    };
    xhr.onprogress = function(event) {
    // event.loaded - how many bytes downloaded
    // event.total - total number of bytes
    alert(`Received ${event.loaded} of ${event.total}`);
    };
    

Program code

Using XHR

function App() {
  const [data, setData] = useState([]);

  useEffect(() => {
    let xhr = new XMLHttpRequest();
    xhr.open("GET", "https://jsonplaceholder.typicode.com/todos");
    xhr.send();
    xhr.onload = function () {
      if (xhr.status !== 200) {
        // console logs the error if request is unsuccessful
        console.log(`Error ${xhr.status}: ${xhr.statusText}`);
      } else {
        // sets the data only if we get a successful response
        // we need to parse the data since the data is obtained in the string type
        setData(JSON.parse(xhr.response));
      }
    };
   xhr.onerror = function() {
     alert("Request failed");
    };
  }, []);
  return (
    <div className="App">
      {data?.map((obj, index) => (
        <div key={index}>{obj.title}</div>
      ))}
    </div>
  );
}

The above code gets the data from the Todos API and displays only the title of each todo on the screen. As you can see in the above code, we had to parse the data to get it in the JSON format. This is because the response is by default in a string type. To avoid the parsing, there is a property called 'responseType' which can be used. After we initialize the constructor, we can add the below statement

xhr.responseType = 'json';

There are various other data types such as 'arraybuffer', 'blob', 'document', but these aren't very commonly used. You can read more about it here

Re-writing the above code using fetch

function App() {
  const [data, setData] = useState([]);
  useEffect(() => {
    fetch("https://jsonplaceholder.typicode.com/todos")
      .then((response) => response.json())
      .then((json) => setData(json))
      .catch((err) => console.log(err));
  }, []);
  return (
    <div className="App">
      {data.map((obj, index) => (
        <div key={index}>{obj.title}</div>
      ))}
    </div>
  );
}

If you compare the above code with the XHR code, you'll see that we need two listeners to be set to handle the success and error cases and a call to open() and send(). But the fetch syntax is pretty simple and easier to understand when compared to XHR.

Why is XHR used in the modern day?

  • The need to support existing scripts with XMLHttpRequest.
  • The need to support old browsers
  • XHR has a few more features when compared to Fetch - onprogress, abort, and timeout

Summary

XHR is similar to fetch and can be used to communicate between the client and server. Although the syntax is such that we need to use multiple properties even for simple API calls, XHR has various properties such as onprogress, timeout, etc which makes event handling pretty easy. Also, a fun fact, Axios uses XHR internally to make server calls. I hope by reading this blog, you understood the basics of XHR. If you want to dive deep into this topic, here are a few resources -