Start · Sprachen · JavaScript · Referenz · XMLHttpRequest: readystatechange

XMLHttpRequest: readystatechange

Ereignis

Wird ausgelöst, sobald sich die Eigenschaft <code>readyState</code> eines <code>XMLHttpRequest</code> ändert.

seit JavaScript Baseline: alle Browser Kategorie: webapi

Signatur

xhr.addEventListener("readystatechange", handler)

Beschreibung

Das Ereignis readystatechange wird bei jedem Wechsel von XMLHttpRequest.readyState ausgelöst. Den Handler registriert man entweder mit xhr.addEventListener("readystatechange", handler) oder über die Eigenschaft xhr.onreadystatechange = handler. Das Ereignis ist ein einfaches Event ohne zusätzliche Eigenschaften, es lässt sich nicht abbrechen (nicht cancelable) und steigt nicht auf (bubbles: false).

Der Wert von readyState durchläuft diese Zustände:

  • 0 – UNSENT: Objekt erzeugt, open() noch nicht aufgerufen
  • 1 – OPENED: open() wurde aufgerufen
  • 2 – HEADERS_RECEIVED: Antwort-Header und Status sind da
  • 3 – LOADING: der Antwortkörper wird geladen, responseText enthält bereits Teildaten
  • 4 – DONE: die Anfrage ist abgeschlossen – erfolgreich oder mit Fehler

Historisch war onreadystatechange der Weg, um auf AJAX-Antworten zu reagieren. Heute sind die spezielleren Ereignisse load, error, progress und loadend meist übersichtlicher, und in neuem Code verwendet man in der Regel gleich fetch(). Nützlich bleibt readystatechange vor allem, um schon bei HEADERS_RECEIVED Header auszuwerten oder um älteren Code zu pflegen.

Parameter

Name Typ Default Beschreibung
$type Pflicht string Der Ereignisname "readystatechange".
$handler Pflicht (event: Event) => void Callback, der ein einfaches Event erhält. event.target bzw. this (bei normalen Funktionen) ist das XMLHttpRequest-Objekt.
$options boolean | AddEventListenerOptions false Optionen wie { once: true } oder { signal } für addEventListener.

Rückgabewert

Typ
void
Beschreibung
addEventListener gibt nichts zurück. Den aktuellen Zustand liest man im Handler aus xhr.readyState.

Beispiele

Klassisch: JSON laden mit onreadystatechange

const xhr = new XMLHttpRequest();
xhr.open("GET", "/api/users/1");
xhr.responseType = "json";

xhr.onreadystatechange = () => {
  // Erst im Zustand DONE (4) ist die Antwort vollständig
  if (xhr.readyState !== XMLHttpRequest.DONE) return;

  if (xhr.status >= 200 && xhr.status < 300) {
    console.log("Benutzer:", xhr.response.name);
  } else {
    console.error("Fehler:", xhr.status || "Netzwerkfehler");
  }
};

xhr.send();
Benutzer: Ada Lovelace

Alle Zustandswechsel mit addEventListener protokollieren

const states = ["UNSENT", "OPENED", "HEADERS_RECEIVED", "LOADING", "DONE"];
const xhr = new XMLHttpRequest();

xhr.addEventListener("readystatechange", () => {
  console.log(`${xhr.readyState} ${states[xhr.readyState]}`);
});

xhr.open("GET", "/daten.txt");
xhr.send();
1 OPENED 2 HEADERS_RECEIVED 3 LOADING 4 DONE

Frühzeitig abbrechen, wenn die Header nicht passen

const xhr = new XMLHttpRequest();

const onStateChange = () => {
  if (xhr.readyState === XMLHttpRequest.HEADERS_RECEIVED) {
    const size = Number(xhr.getResponseHeader("Content-Length"));
    if (size > 5_000_000) {
      console.warn(`Datei zu groß (${size} Bytes) – Abbruch`);
      xhr.abort();
    }
  }
  if (xhr.readyState === XMLHttpRequest.DONE) {
    // Listener entfernen, sobald er nicht mehr gebraucht wird
    xhr.removeEventListener("readystatechange", onStateChange);
  }
};

xhr.addEventListener("readystatechange", onStateChange);
xhr.open("GET", "/downloads/video.mp4");
xhr.send();
Datei zu groß (48213000 Bytes) – Abbruch

In ein Promise verpacken

const request = (method, url) =>
  new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open(method, url);
    xhr.onreadystatechange = () => {
      if (xhr.readyState !== 4) return;
      xhr.status >= 200 && xhr.status < 300
        ? resolve(xhr.responseText)
        : reject(new Error(`HTTP ${xhr.status}`));
    };
    xhr.send();
  });

const text = await request("GET", "/hallo.txt");
console.log(text);
Hallo Welt

// Wichtig · Fallstricke

  • Status 0 bei DONE: Bei Netzwerkfehlern, CORS-Blockaden oder abort() wird ebenfalls readyState === 4 erreicht, aber mit status === 0. Prüfe deshalb immer auch status – oder nutze zusätzlich die Ereignisse error und abort.
  • LOADING kann mehrfach feuern: Zustand 3 kann je nach Browser bei jedem empfangenen Datenblock erneut gemeldet werden. Für Fortschrittsanzeigen ist progress besser geeignet.
  • Synchrone Anfragen: Bei open(method, url, false) werden nicht alle Zwischenzustände zuverlässig gemeldet. Synchrone XHR im Hauptthread sind außerdem veraltet und blockieren die Oberfläche.
  • Reihenfolge: onreadystatechange sollte vor send() (am besten vor open()) gesetzt werden, sonst gehen frühe Zustandswechsel verloren. Eine erneute Zuweisung der on-Eigenschaft überschreibt den vorherigen Handler, addEventListener dagegen erlaubt mehrere Handler.
  • Kompatibilität: Wird von allen Browsern unterstützt, auch in Web Workern. Service Worker haben kein XMLHttpRequest – dort nur fetch(). In Node.js gibt es XHR nicht nativ.