Ad – 728Γ—90
🌐 DOM & Browser

JavaScript AJAX – Asynchronous Data Loading

AJAX (Asynchronous JavaScript And XML) is a technique for exchanging data with a server in the background without reloading the page. It transformed the web from static documents to dynamic applications. Today AJAX is implemented with the Fetch API, but understanding the original XMLHttpRequest is still valuable for legacy codebases and interviews.

⏱️ 19 min read 🎯 Intermediate πŸ“… Updated 2026

What Is AJAX?

AJAX is not a language or library β€” it is a technique combining:

  • JavaScript β€” orchestrates the request and handles the response
  • XMLHttpRequest or Fetch API β€” sends the HTTP request
  • JSON (or XML) β€” the data format exchanged (JSON dominates today)
  • DOM manipulation β€” updates the page with the response data

Classic use cases include live search suggestions, infinite scroll, form submission without reload, and real-time dashboards.

XMLHttpRequest (XHR)

XHR was the only AJAX mechanism for over a decade. Understanding it explains why Fetch was created:

JavaScript
// XHR GET request
const xhr = new XMLHttpRequest();

// 1. Configure: method, URL, async (always true in modern code)
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);

// 2. Set response type
xhr.responseType = 'json'; // auto-parse JSON

// 3. Attach event handlers
xhr.onreadystatechange = function() {
  // readyState: 0=UNSENT, 1=OPENED, 2=HEADERS_RECEIVED, 3=LOADING, 4=DONE
  if (xhr.readyState === XMLHttpRequest.DONE) {
    if (xhr.status >= 200 && xhr.status < 300) {
      console.log('Response:', xhr.response);
    } else {
      console.error('Error:', xhr.status, xhr.statusText);
    }
  }
};

xhr.onerror = () => console.error('Network error');

// 4. Send the request
xhr.send();
β–Ά Output
Response: { userId: 1, id: 1, title: "sunt aut facere repellat...", body: "..." }

XHR POST Request

JavaScript
function xhrPost(url, data, callback) {
  const xhr = new XMLHttpRequest();
  xhr.open('POST', url, true);
  xhr.setRequestHeader('Content-Type', 'application/json');
  xhr.responseType = 'json';

  xhr.onload = function() {
    if (xhr.status >= 200 && xhr.status < 300) {
      callback(null, xhr.response);
    } else {
      callback(new Error(`HTTP ${xhr.status}`), null);
    }
  };

  xhr.onerror = () => callback(new Error('Network error'), null);

  xhr.send(JSON.stringify(data));
}

// Usage
xhrPost(
  'https://jsonplaceholder.typicode.com/posts',
  { title: 'AJAX Post', body: 'Posted via XHR', userId: 1 },
  (err, data) => {
    if (err) { console.error(err); return; }
    console.log('Created:', data);
  }
);

readyState and Status Codes

ValueConstantMeaning
readyState
0UNSENTopen() not called yet
1OPENEDopen() called
2HEADERS_RECEIVEDsend() called; headers received
3LOADINGResponse body downloading
4DONEOperation complete
Common HTTP Status Codes
200OKSuccess
201CreatedResource created (POST)
204No ContentSuccess, no body (DELETE)
400Bad RequestInvalid request data
401UnauthorizedAuthentication required
404Not FoundResource does not exist
500Internal Server ErrorServer-side bug

Modern AJAX with Fetch

Fetch achieves the same goals with far less boilerplate:

JavaScript
// Live search using Fetch
const searchInput = document.getElementById('search');
const resultsList = document.getElementById('results');

let debounceTimer;

searchInput.addEventListener('input', () => {
  clearTimeout(debounceTimer);
  debounceTimer = setTimeout(async () => {
    const query = searchInput.value.trim();
    if (!query) { resultsList.innerHTML = ''; return; }

    try {
      resultsList.innerHTML = '<li>Loading...</li>';
      const res  = await fetch(`/api/search?q=${encodeURIComponent(query)}`);
      if (!res.ok) throw new Error(`HTTP ${res.status}`);
      const data = await res.json();

      resultsList.innerHTML = data.length
        ? data.map(item => `<li>${item.name}</li>`).join('')
        : '<li>No results found</li>';

    } catch (err) {
      resultsList.innerHTML = `<li class="error">Error: ${err.message}</li>`;
    }
  }, 300); // 300 ms debounce
});

Axios – A Popular AJAX Library

Axios is a third-party library that wraps XHR/Fetch with extra features: automatic JSON parsing, interceptors, request cancellation, and better error handling out of the box:

JavaScript
// Include via CDN: <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
// Or install: npm install axios

// GET β€” Axios auto-parses JSON
async function getUsers() {
  try {
    const { data } = await axios.get('https://jsonplaceholder.typicode.com/users');
    console.log(data); // already parsed array
  } catch (error) {
    // Axios DOES reject on HTTP errors (unlike native fetch)
    console.error(error.response.status, error.message);
  }
}

// POST
async function createUser(userData) {
  const { data } = await axios.post('/api/users', userData, {
    headers: { Authorization: 'Bearer my-token' }
  });
  return data;
}

// Axios instance with base URL and default headers
const api = axios.create({
  baseURL: 'https://api.example.com',
  timeout: 10000,
  headers: { 'X-API-Key': 'abc123' }
});

// Use the instance
const { data: profile } = await api.get('/profile');
ℹ️
When to use Axios vs Fetch

Use the native Fetch API for new projects to avoid extra dependencies β€” it handles 95% of use cases. Choose Axios when you need: request/response interceptors, progress events for file uploads, automatic HTTP error rejection, or when supporting environments where fetch isn't available natively.

CORS Policy Explained

Browsers enforce the Same-Origin Policy: a page at https://myapp.com cannot freely request https://api.other.com unless that server explicitly allows it via CORS headers.

JavaScript
// This will be BLOCKED by the browser if api.other.com doesn't allow it
fetch('https://api.other.com/data')
  .catch(err => console.error(err)); // TypeError: Failed to fetch

// The server must respond with:
// Access-Control-Allow-Origin: https://myapp.com
// (or * for any origin)

// Preflight: for POST/PUT with custom headers, browser sends an OPTIONS
// request first to check permissions β€” Fetch handles this automatically.

// Workarounds (when you can't control the server):
// 1. A proxy server on YOUR domain that forwards requests
// 2. JSONP (legacy, only for GET, avoid in new code)

// Example: proxy pattern
const PROXY = 'https://myapp.com/proxy?url=';
fetch(PROXY + encodeURIComponent('https://api.other.com/data'))
  .then(r => r.json())
  .then(data => console.log(data));

πŸ‹οΈ Practical Exercise

Build an infinite-scroll post list:

  1. On page load, fetch the first 10 posts from https://jsonplaceholder.typicode.com/posts?_start=0&_limit=10.
  2. Render each post as a card.
  3. When the user scrolls near the bottom of the page (use a scroll listener on window and compare scrollY + innerHeight to document.body.scrollHeight), fetch the next 10 posts.
  4. Show a loading spinner while fetching; stop when all 100 posts are loaded.

πŸ”₯ Challenge Exercise

Build a GitHub user lookup tool using the public GitHub API (https://api.github.com/users/{username}). Features: text input + search button, display avatar, name, bio, followers, public repos; below that, load the user's top 5 repos by stars from /users/{username}/repos?sort=stars&per_page=5. Handle 404 (user not found) and rate limit (403) errors with appropriate messages. Use XHR for the user lookup and Fetch for the repos β€” deliberately mixing both for practice.

Summary

πŸ“‹ Summary

  • AJAX is a technique for loading data asynchronously without page reloads.
  • XHR uses open(), send(), and onreadystatechange; check readyState === 4 and status.
  • Modern AJAX uses the Fetch API with Promises and async/await.
  • Axios provides a cleaner API, auto JSON parsing, and HTTP error rejection.
  • CORS is enforced by the browser; the server must send Access-Control-Allow-Origin headers.
  • Debounce AJAX calls in search inputs to avoid excessive requests.
  • JSONP is a legacy workaround β€” avoid in new code; use a proxy instead.

Interview Questions

  • What does AJAX stand for and what problem does it solve?
  • What are the readyState values in XHR and what does each mean?
  • How does Axios differ from the native Fetch API?
  • What is a CORS preflight request?
  • How would you debounce an AJAX search request?

Frequently Asked Questions

Is AJAX still relevant with modern frameworks? +

Absolutely. React, Vue, and Angular all need to fetch data from APIs β€” they just abstract the Fetch or Axios calls into hooks or services. Understanding AJAX fundamentals makes you far more effective when debugging network issues, writing custom data-fetching logic, or working without a framework.

What is JSONP and why should I avoid it? +

JSONP (JSON with Padding) is a legacy hack that injects a <script> tag pointing to a cross-origin URL. The server wraps the JSON in a callback function call, bypassing CORS. It only works for GET requests, is vulnerable to XSS if the server is compromised, and is completely superseded by CORS and proxy patterns. Don't use it in new code.

How do I show upload progress with Fetch? +

The Fetch API does not natively expose upload progress events. For upload progress, use XMLHttpRequest with xhr.upload.addEventListener('progress', handler), or use the Axios library which wraps XHR and exposes an onUploadProgress callback. The Streams API (via fetch request body as a ReadableStream) can provide some progress info but is experimental in most environments.

Ad – 336Γ—280