Đối tượng XMLHttpRequest - AJAX trong JavaScript

Javascript nâng cao | by Học Javascript

Trong thời đại web hiện đại, trải nghiệm người dùng mượt mà và phản hồi nhanh chóng là yếu tố then chốt để giữ chân người truy cập. Một trong những công nghệ nền tảng giúp đạt được điều đó chính là AJAX (Asynchronous JavaScript and XML) – kỹ thuật cho phép gửi và nhận dữ liệu từ server mà không cần tải lại toàn bộ trang web.

Trước khi các API hiện đại như fetch() ra đời, công cụ chủ lực giúp AJAX hoạt động chính là đối tượng XMLHttpRequest. Đây là một thành phần cốt lõi trong JavaScript, cho phép các lập trình viên xây dựng các ứng dụng web linh hoạt, phản hồi tức thì theo hành động của người dùng. Trong bài viết này, chúng ta sẽ tìm hiểu chi tiết về XMLHttpRequest, cách sử dụng, ứng dụng thực tế cũng như các điểm cần lưu ý khi làm việc với đối tượng này.

Đối tượng XMLHttpRequest là gì?

XMLHttpRequest là một đối tượng được tích hợp sẵn trong trình duyệt, cho phép gửi các yêu cầu HTTP/HTTPS đến server và nhận dữ liệu phản hồi một cách bất đồng bộ. Đây là công nghệ cốt lõi của kỹ thuật AJAX, giúp trang web có thể giao tiếp với server phía sau hậu trường mà không cần tải lại toàn bộ trang – mang lại trải nghiệm mượt mà và nhanh chóng cho người dùng.

Một số đặc điểm chính của XMLHttpRequest:

  • Được cung cấp bởi trình duyệt, không cần thư viện ngoài.

  • Cho phép gửi và nhận dữ liệu từ server thông qua các phương thức HTTP như: GET, POST, PUT, DELETE, v.v.

  • Hỗ trợ giao tiếp đồng bộ hoặc bất đồng bộ, tuy nhiên bất đồng bộ là chế độ mặc định và khuyến khích sử dụng.

  • Có thể xử lý dữ liệu dưới nhiều định dạng như văn bản thuần (text), XML, hoặc JSON (phân tích thủ công).

Vì sao XMLHttpRequest quan trọng?

Trước khi các giải pháp hiện đại như Fetch API hay thư viện như Axios phổ biến, XMLHttpRequest là phương tiện duy nhất và chuẩn mực để thực hiện gọi AJAX. Nắm vững XMLHttpRequest không chỉ giúp hiểu rõ về cách AJAX hoạt động mà còn hữu ích khi cần tương thích với các trình duyệt cũ hoặc môi trường đặc biệt không hỗ trợ các API mới.

Các bước sử dụng XMLHttpRequest trong JavaScript

Để sử dụng XMLHttpRequest trong JavaScript, bạn cần thực hiện tuần tự theo 4 bước chính: tạo đối tượng, cấu hình yêu cầu, xử lý phản hồi, và gửi yêu cầu. Cụ thể như sau:

Tạo đối tượng XMLHttpRequest

Đầu tiên, bạn cần khởi tạo một thể hiện mới của XMLHttpRequest:

const xhr = new XMLHttpRequest();

Đối tượng xhr sẽ dùng để thiết lập và gửi yêu cầu HTTP cũng như nhận và xử lý phản hồi từ server.

Cấu hình yêu cầu

Sử dụng phương thức .open() để cấu hình yêu cầu mà bạn sắp gửi. Cú pháp:

xhr.open(method, url, async);
  • method: Phương thức HTTP, thường là "GET" hoặc "POST".

  • url: Đường dẫn đến tài nguyên hoặc API cần truy cập.

  • async: Tham số boolean (true hoặc false) cho biết có muốn gửi yêu cầu bất đồng bộ hay không (nên để true để không chặn giao diện người dùng).

Ví dụ:

xhr.open("GET", "https://api.example.com/data", true);

Xử lý phản hồi từ server

Bạn có thể xử lý phản hồi bằng một trong hai cách phổ biến:

Dùng onreadystatechange (cách truyền thống):

xhr.onreadystatechange = function () {
  if (xhr.readyState === 4 && xhr.status === 200) {
    const data = JSON.parse(xhr.responseText);
    console.log("Dữ liệu nhận được:", data);
  }
};
  • xhr.readyState === 4: Đảm bảo yêu cầu đã hoàn tất.

  • xhr.status === 200: Kiểm tra server phản hồi thành công.

  • xhr.responseText: Nội dung phản hồi (thường là JSON hoặc text).

Hoặc dùng onload (cách hiện đại và dễ hiểu hơn):

xhr.onload = function () {
  if (xhr.status === 200) {
    const data = JSON.parse(xhr.responseText);
    console.log("Dữ liệu nhận được:", data);
  } else {
    console.error("Lỗi:", xhr.status);
  }
};

Gửi yêu cầu đến server

Cuối cùng, dùng .send() để thực sự gửi request:

xhr.send();
Nếu bạn sử dụng POST, bạn có thể truyền thêm dữ liệu trong send():
xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify({ name: "Đậu Trung Kiên" }));

Các thuộc tính và phương thức quan trọng trong JavaScript

Khi sử dụng đối tượng XMLHttpRequest, bạn sẽ thường xuyên tương tác với một số phương thức và thuộc tính quan trọng để thiết lập, gửi yêu cầu, và xử lý phản hồi từ server. Cụ thể như sau:

.open(method, url, async)

Dùng để thiết lập yêu cầu HTTP cần gửi. Cú pháp:

xhr.open("GET", "https://api.example.com/data", true);
  • method: Phương thức HTTP như "GET", "POST", "PUT", "DELETE"...

  • url: Địa chỉ tài nguyên cần truy cập.

  • async: Có muốn gửi yêu cầu bất đồng bộ hay không (nên để true).

.send([body])

Dùng để gửi yêu cầu đến server sau khi đã thiết lập xong.

  • Với phương thức GET: không cần truyền tham số trong .send()

xhr.send();

Với POST: bạn có thể truyền dữ liệu (body) dưới dạng chuỗi JSON, form-encoded...

xhr.setRequestHeader("Content-Type", "application/json");
xhr.send(JSON.stringify({ name: "Đậu Trung Kiên" }));

.setRequestHeader(name, value)

Thiết lập tiêu đề HTTP (header) cho yêu cầu trước khi gọi .send().

Ví dụ: Gửi dữ liệu JSON:

xhr.setRequestHeader("Content-Type", "application/json");

Hoặc xác thực bằng token:

xhr.setRequestHeader("Authorization", "Bearer abc123");

.readyState

Thuộc tính cho biết trạng thái hiện tại của yêu cầu. Có 5 trạng thái (số nguyên từ 0 đến 4):

Giá trị Trạng thái Ý nghĩa
0 UNSENT Đối tượng đã được tạo nhưng chưa cấu hình.
1 OPENED Đã gọi .open() nhưng chưa gửi.
2 HEADERS_RECEIVED Đã nhận phản hồi header từ server.
3 LOADING Đang tải dữ liệu từ server.
4 DONE Dữ liệu đã tải xong, phản hồi đã sẵn sàng.

Phổ biến nhất là kiểm tra readyState === 4 để đảm bảo phản hồi đã đầy đủ.

.status.statusText

Thông tin về trạng thái HTTP của phản hồi từ server:

  • .status: Mã trạng thái (200, 404, 500…)

  • .statusText: Chuỗi mô tả tương ứng với mã trạng thái ("OK", "Not Found", "Internal Server Error"...)

Ví dụ:

if (xhr.readyState === 4) {
  console.log(xhr.status);      // Ví dụ: 200
  console.log(xhr.statusText);  // Ví dụ: OK
}

.responseText.responseXML

  • .responseText: Dữ liệu phản hồi dưới dạng văn bản (chuỗi) — thường là JSON, HTML, hoặc plain text.

const data = JSON.parse(xhr.responseText);

.responseXML: Dữ liệu phản hồi dưới dạng tài liệu XML DOM, nếu server trả về nội dung XML.

const xmlDoc = xhr.responseXML;

Ví dụ minh họa cơ bản trong JavaScript

Ví dụ 1: Gửi yêu cầu GET đến server và hiển thị dữ liệu

<button onclick="getData()">Lấy dữ liệu</button>
<div id="output"></div>

<script>
  function getData() {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", "https://jsonplaceholder.typicode.com/posts/1", true);

    xhr.onload = function () {
      if (xhr.status === 200) {
        const data = JSON.parse(xhr.responseText);
        document.getElementById("output").innerHTML = `
          <h3>${data.title}</h3>
          <p>${data.body}</p>
        `;
      } else {
        document.getElementById("output").textContent = "Không lấy được dữ liệu.";
      }
    };

    xhr.onerror = function () {
      document.getElementById("output").textContent = "Lỗi kết nối đến server.";
    };

    xhr.send();
  }
</script>

Giải thích:

  • Gửi yêu cầu GET đến API mẫu.

  • Nếu thành công (status === 200), hiển thị dữ liệu trong giao diện.

  • Nếu có lỗi hoặc không lấy được, báo lỗi cho người dùng.

Ví dụ 2: Gửi POST chứa dữ liệu JSON đến server

<button onclick="sendData()">Gửi dữ liệu</button>

<script>
  function sendData() {
    const xhr = new XMLHttpRequest();
    xhr.open("POST", "https://jsonplaceholder.typicode.com/posts", true);
    xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");

    xhr.onload = function () {
      if (xhr.status === 201) {
        const data = JSON.parse(xhr.responseText);
        console.log("Phản hồi từ server:", data);
        alert("Gửi dữ liệu thành công!");
      } else {
        alert("Không gửi được dữ liệu.");
      }
    };

    const body = JSON.stringify({
      title: "Đây là tiêu đề",
      body: "Đây là nội dung",
      userId: 1
    });

    xhr.send(body);
  }
</script>

Giải thích:

  • Thiết lập yêu cầu POST với header "Content-Type: application/json".

  • Dữ liệu được gửi lên dưới dạng JSON.

  • Kiểm tra status trả về từ server (201 Created).

Ví dụ 3: Xử lý lỗi HTTP và cập nhật giao diện

<button onclick="loadInvalidURL()">Thử lỗi</button>
<div id="errorOutput"></div>

<script>
  function loadInvalidURL() {
    const xhr = new XMLHttpRequest();
    xhr.open("GET", "https://jsonplaceholder.typicode.com/invalid-url", true);

    xhr.onload = function () {
      if (xhr.status === 404) {
        document.getElementById("errorOutput").textContent = "Lỗi 404: Không tìm thấy tài nguyên.";
      } else {
        document.getElementById("errorOutput").textContent = "Trạng thái khác: " + xhr.status;
      }
    };

    xhr.onerror = function () {
      document.getElementById("errorOutput").textContent = "Không thể kết nối đến server.";
    };

    xhr.send();
  }
</script>

Giải thích:

  • Gửi GET đến một đường dẫn sai để kiểm tra phản hồi lỗi.

  • Xử lý lỗi HTTP 404 và báo thông tin rõ ràng cho người dùng.

  • .onerror dùng để xử lý lỗi kết nối (mạng bị ngắt, không phản hồi…).

So sánh XMLHttpRequest và Fetch API trong JavaScript

Khi làm việc với các tác vụ gửi yêu cầu đến server trong JavaScript, chúng ta có hai lựa chọn phổ biến: XMLHttpRequest (XHR) và Fetch API. Mỗi công nghệ đều có những điểm mạnh và hạn chế riêng.

Ưu điểm của XMLHttpRequest

  • Tương thích trình duyệt cũ:
    XMLHttpRequest được hỗ trợ bởi hầu hết mọi trình duyệt, kể cả các phiên bản cũ như Internet Explorer. Điều này khiến nó trở thành lựa chọn an toàn cho các ứng dụng cần tương thích rộng.

  • Kiểm soát chi tiết:
    XHR cho phép kiểm soát từng bước của tiến trình yêu cầu thông qua thuộc tính .readyState. Ngoài ra, có thể sử dụng các phương thức như setRequestHeader(), theo dõi trạng thái mạng, xử lý tiến trình tải (onprogress), hủy yêu cầu (abort()),...

Nhược điểm của XMLHttpRequest

  • Cú pháp dài dòng và phức tạp:
    Việc sử dụng XMLHttpRequest thường đòi hỏi viết nhiều dòng mã với nhiều hàm callback lồng nhau (callback hell), khiến code khó đọc và khó bảo trì.

  • Không hỗ trợ Promise natively:
    XMLHttpRequest không hỗ trợ Promise, nên không thể sử dụng cú pháp async/await trực tiếp. Muốn làm được điều đó, cần bọc nó trong một hàm Promise thủ công — điều này làm tăng độ phức tạp.

Ưu điểm của Fetch API

Cú pháp hiện đại, ngắn gọn hơn:
Fetch API sử dụng Promise nên có thể kết hợp với .then() hoặc async/await, giúp viết code gọn gàng, dễ hiểu.

const response = await fetch(url);
const data = await response.json();
  • Tích hợp tốt với async/await:
    Việc kết hợp fetch với async/await giúp quá trình xử lý dữ liệu tuần tự trở nên rõ ràng hơn so với việc phải xử lý sự kiện như trong XHR.

  • Dễ mở rộng và bảo trì:
    Vì dựa trên Promise nên fetch dễ kết hợp với nhiều tác vụ xử lý bất đồng bộ khác trong ứng dụng web.

Nhược điểm của Fetch API

  • Không hỗ trợ trên một số trình duyệt cũ:
    Fetch không được hỗ trợ trên Internet Explorer, do đó nếu cần tương thích hoàn toàn với trình duyệt cũ, vẫn phải dùng XHR hoặc cần polyfill.

  • Không tự động "bắt lỗi" HTTP:
    fetch() sẽ không ném lỗi (throw) nếu server trả về HTTP status 4xx hoặc 5xx. Bạn phải tự kiểm tra response.ok hoặc response.status để xử lý các lỗi HTTP này.

const res = await fetch(url);
if (!res.ok) {
  throw new Error("Có lỗi xảy ra với HTTP status: " + res.status);
}

Ứng dụng thực tế của XMLHttpRequest trong JavaScript

Mặc dù hiện nay Fetch API đã phổ biến hơn, XMLHttpRequest vẫn được sử dụng trong nhiều hệ thống hiện có nhờ khả năng tương thích và kiểm soát chi tiết trong giao tiếp HTTP. Dưới đây là một số ứng dụng điển hình:

Gợi ý tìm kiếm theo thời gian thực (Live Search)

  • Khi người dùng nhập văn bản vào ô tìm kiếm, JavaScript có thể sử dụng XMLHttpRequest để gửi yêu cầu tìm kiếm đến server sau mỗi vài ký tự.

  • Server sẽ trả về danh sách gợi ý phù hợp mà không cần tải lại toàn bộ trang.

  • Giúp cải thiện trải nghiệm người dùng nhờ phản hồi nhanh chóng.

Ví dụ: Gợi ý tên sản phẩm, địa điểm, từ khóa liên quan,...

Tự động cập nhật nội dung mà không tải lại trang

  • Sử dụng XHR để định kỳ gửi yêu cầu (thường kết hợp với setInterval) nhằm lấy dữ liệu mới từ server.

  • Nội dung sẽ được cập nhật trực tiếp trên trang web mà không cần reload, tạo ra trải nghiệm mượt mà và liền mạch.

Ví dụ: Bảng xếp hạng trực tiếp, cập nhật giá chứng khoán, thông báo tin nhắn mới...

Tải dữ liệu trang động (comments, bài viết, sản phẩm…)

  • XHR có thể được dùng để tải một phần nội dung như danh sách bình luận, bài viết liên quan, hoặc sản phẩm ở phần tiếp theo (pagination / load more).

  • Giúp tiết kiệm băng thông, tăng tốc độ tải trang ban đầu.

Ví dụ: Khi cuộn xuống cuối trang, trang web gửi yêu cầu qua XHR để tải thêm bài viết (lazy load).

Một số lưu ý khi dùng XMLHttpRequest trong JavaScript

Để sử dụng XMLHttpRequest hiệu quả và an toàn, bạn cần lưu ý những điều sau:

Xử lý lỗi mạng và lỗi từ server

  • XMLHttpRequest không tự động ném lỗi khi có lỗi HTTP.

  • Cần kiểm tra xhr.status để xác định thành công hay thất bại.

  • Sử dụng onerror để bắt lỗi kết nối (ví dụ: mất mạng, không truy cập được server).

xhr.onerror = function() {
  console.error("Lỗi kết nối đến server.");
};

Chú ý bảo mật khi gửi dữ liệu (đặc biệt với POST)

  • Khi gửi thông tin đăng nhập, thông tin người dùng qua POST, cần mã hóa hoặc sử dụng giao thức HTTPS để đảm bảo dữ liệu không bị chặn hoặc đọc lén.

  • Đừng quên sử dụng Content-Type thích hợp, và tránh để lộ thông tin nhạy cảm trong URL khi dùng GET.

xhr.setRequestHeader("Content-Type", "application/json");

Thêm trạng thái loading và thông báo cho người dùng

  • Nên thông báo rõ cho người dùng biết rằng dữ liệu đang được tải (hiển thị spinner, text “Đang tải…”).

  • Sau khi hoàn thành, hiển thị kết quả hoặc thông báo nếu có lỗi.

  • Tránh để người dùng “đoán” không biết trang đang làm gì → cải thiện UX đáng kể.

document.getElementById("loading").style.display = "block";

xhr.onload = function () {
  document.getElementById("loading").style.display = "none";
  // xử lý dữ liệu
};

Kết bài

XMLHttpRequest là nền tảng quan trọng giúp AJAX hoạt động, mở ra khả năng giao tiếp bất đồng bộ giữa trình duyệt và máy chủ mà không cần tải lại toàn bộ trang. Dù ngày nay Fetch API đang dần thay thế nhờ cú pháp hiện đại hơn, nhưng XMLHttpRequest vẫn là một công cụ mạnh mẽ và được sử dụng rộng rãi trong nhiều hệ thống, đặc biệt là những hệ thống cần hỗ trợ trình duyệt cũ hoặc yêu cầu kiểm soát chi tiết trong luồng xử lý.

Việc nắm vững cách sử dụng XMLHttpRequest, từ cách khởi tạo yêu cầu đến xử lý phản hồi, lỗi và bảo mật, sẽ giúp lập trình viên xây dựng các ứng dụng web mượt mà, hiệu quả và thân thiện với người dùng. Đây chính là bước đệm quan trọng để tiến tới những kỹ thuật AJAX hiện đại hơn như Fetch hay các thư viện như Axios trong tương lai.

Bài viết liên quan