Chapter 1 of ?
js 8 min read

JavaScript Mastery — Chapter 36: Legacy JS & AJAX

Module 9: Legacy Technologies & Frameworks • Chapter 36

Legacy JS & AJAX

Understand the foundational asynchronous revolution: XMLHttpRequest (XHR) state machines, the 5 readyState codes, XML parsing via DOMParser, and bridging legacy legacy enterprise codebases to modern Fetch.

Classical Full Page Reload vs Asynchronous AJAX

Before AJAX, clicking a link or submitting a form required the web browser to destroy the current page and render a blank white screen while downloading the entire new HTML document from scratch.

1. User Interaction Form Submit Full HTTP POST White Flash Screen Entire Page Destroyed Full HTML Reload New Page Rendered CSS & Scripts re-executed 1. User Interaction Button Click Background XHR Page Uninterrupted Zero flicker or reload JSON / XML Data Partial DOM Update Only changed widget rerenders

1. The XMLHttpRequest State Machine (5 ReadyStates)

Unlike fetch() which relies on Promises, XMLHttpRequest is an event-driven state machine. As the request progresses from initial setup to response arrival, its readyState transitions through five numerical states:

readyState State Constant Description & Available Properties
0 XMLHttpRequest.UNSENT Client object instantiated, but open() has not yet been called.
1 XMLHttpRequest.OPENED open(method, url) has been invoked. Request headers can now be set via setRequestHeader().
2 XMLHttpRequest.HEADERS_RECEIVED send() has been called. Response headers and HTTP status codes are now accessible.
3 XMLHttpRequest.LOADING Response payload is actively streaming. responseText contains partial bytes.
4 XMLHttpRequest.DONE Data transfer complete. Full payload is ready for inspection.

The Canonical Classic XHR Pattern

const xhr = new XMLHttpRequest();

// Configure HTTP method, target endpoint, and async boolean
xhr.open('GET', 'https://api.example.com/users', true);

// Set custom request headers (MUST be after open, before send)
xhr.setRequestHeader('Accept', 'application/json');

// Listen to state transitions
xhr.onreadystatechange = function () {
  if (xhr.readyState === XMLHttpRequest.DONE) {
    if (xhr.status === 200) {
      const data = JSON.parse(xhr.responseText);
      console.log('Received data:', data);
    } else {
      console.error(`HTTP error: ${xhr.status}`);
    }
  }
};

// Initiate network socket dispatch
xhr.send();
Run in Playground

2. Upload Progress Tracking & XML Parsing

Even today in modern web development, XMLHttpRequest has one unique capability that modern fetch() cannot easily match without complex Streams: accurate upload progress monitoring.

Tracking File Upload Progress
xhr.upload.onprogress = function (e) {
  if (e.lengthComputable) {
    const percent = (e.loaded / e.total) * 100;
    progressBar.style.width = `${percent}%`;
  }
};
Parsing XML Payloads via DOMParser
const parser = new DOMParser();
const xmlDoc = parser.parseFromString(xmlString, 'text/xml');
const title = xmlDoc.getElementsByTagName('title')[0].textContent;

Interactive Lab: XHR ReadyState Simulator

State Machine

Step through an XMLHttpRequest lifecycle step-by-step. Witness how internal properties like readyState, status, and responseText materialize.

0: UNSENT (new XMLHttpRequest())
1: OPENED (xhr.open('GET', url))
2: HEADERS_RECEIVED (Status: 200)
3: LOADING (Streaming payload...)
4: DONE (Transfer Complete)
XHR OBJECT TELEMETRY:
readyState: 0 (UNSENT) status: 0 statusText: "" responseText: ""
Hands-on Challenge

Promisify XMLHttpRequest into a Modern Async Utility

In legacy maintenance work, you will frequently need to modernize legacy callback-based XHR endpoints. Wrap XMLHttpRequest inside a modern ajax(url, options) function that returns a Promise resolving with the parsed JSON data or rejecting on error or non-2xx status codes.

function ajax(url, { method = 'GET', headers = {}, body = null } = {}) {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open(method, url, true);

    // Set request headers
    Object.entries(headers).forEach(([key, val]) => {
      xhr.setRequestHeader(key, val);
    });

    xhr.onload = () => {
      if (xhr.status >= 200 && xhr.status < 300) {
        try {
          resolve(JSON.parse(xhr.responseText));
        } catch {
          resolve(xhr.responseText);
        }
      } else {
        reject(new Error(`HTTP Error ${xhr.status}: ${xhr.statusText}`));
      }
    };

    xhr.onerror = () => reject(new Error('Network error occurred'));
    xhr.ontimeout = () => reject(new Error('Request timed out'));

    xhr.send(body);
  });
}

Chapter 36 Knowledge Check

Validate your understanding of legacy AJAX, readyStates, and network communication.

1. What does readyState value 4 signify on an XMLHttpRequest instance?
The request was aborted
The operation is complete (DONE) and response data is ready
HTTP 404 Not Found error
Connection opened but headers not received
2. When configuring an XMLHttpRequest, at what point MUST xhr.setRequestHeader() be called?
Before xhr.open()
After xhr.open(), but before xhr.send()
Inside the onreadystatechange handler
After xhr.send()
3. What is one persistent capability where XMLHttpRequest is still used over standard fetch()?
XHR runs 10x faster
Direct, simple upload progress event tracking via xhr.upload.onprogress
XHR bypasses Cross-Origin Resource Sharing (CORS)
XHR does not require JavaScript
4. Which built-in browser API parses raw XML text strings into an XML DOM document?
JSON.parse()
DOMParser
XMLSerializer
eval()
5. What does the acronym AJAX stand for?
Asynchronous JavaScript and XML
Advanced JavaScript and XHTML
Automated Java Application eXecution
Asynchronous JSON and XHTML
Done with this chapter?
Mark it complete to track your progress and unlock your certificate.
Next Up
—

Learner Reviews

Write a Review
Share your experience to help other learners.
Your Rating *
★ ★ ★ ★ ★