XMLHttpRequest
XMLHttpRequest (XHR) in JavaScript: open und send, readyState, load/error/timeout-Events, JSON parsen, POST-Daten, Anfragen abbrechen und Vergleich mit der Fetch API.
JavaScript ist eine unverzichtbare Programmiersprache für die Webentwicklung, die dynamische und interaktive Benutzererlebnisse ermöglicht. Eine der wichtigsten Funktionen von JavaScript ist die Fähigkeit, mit Servern zu kommunizieren, Daten abzurufen und Webseiten asynchron zu aktualisieren. Dies wird hauptsächlich durch den Einsatz von XMLHttpRequest (XHR) erreicht. Dieser Artikel bietet einen tiefen Einblick in XMLHttpRequest, einschließlich seiner Methoden, Eigenschaften und praktischen Anwendungen, mit mehreren Codebeispielen zur Unterstützung des Lernens.
XMLHttpRequest arbeitet mit Callback-Funktionen. Für neuen Code greift man üblicherweise zur Fetch API, die auf Promises basiert und sauber mit async/await zusammenarbeitet. XHR lohnt sich dennoch zu verstehen: Man begegnet ihm in älteren Codebasen, und es ist die einzige eingebaute API, die detaillierte Upload-Fortschrittsberichte liefert.
Diese Seite behandelt, was ein XHR-Objekt ist, wie man eine Anfrage mit open und send konfiguriert und sendet, wie man die Antwort über readyState und die load/error/timeout-Events liest, wie man JSON parst, wie man Daten per POST sendet, wie man eine Anfrage abbricht und wie XHR im Vergleich zu fetch abschneidet.
XMLHttpRequest verstehen
XMLHttpRequest (XHR) ist ein eingebautes Browser-Objekt, das JavaScript ermöglicht, eine HTTP- oder HTTPS-Anfrage an einen Server zu senden und die Antwort zu empfangen, ohne die Seite neu zu laden. Das „XML" im Namen ist historisch bedingt – XHR kann jedes Text- oder Binärformat übertragen, und JSON ist heute bei weitem das gebräuchlichste. Diese Fähigkeit, im Hintergrund mit einem Server zu kommunizieren, bildet die Grundlage dessen, was früher als AJAX (Asynchronous JavaScript and XML) bezeichnet wurde.
Der Lebenszyklus einer Anfrage ist immer gleich: Objekt erstellen, es öffnen (Methode und URL konfigurieren), Event-Handler anhängen, um auf das Ergebnis zu reagieren, und es dann senden.
Ein XMLHttpRequest-Objekt erstellen
Zunächst wird eine Instanz erstellt:
const xhr = new XMLHttpRequest();Eine einzelne XMLHttpRequest-Instanz verwaltet eine Anfrage. Um eine zweite Anfrage zu stellen, muss ein neues Objekt erstellt werden.
Eine HTTP-Anfrage stellen
Sobald das Objekt existiert, wird es mit open konfiguriert und anschließend mit send abgeschickt.
Die open-Methode
open initialisiert eine Anfrage, sendet sie aber noch nicht ab. Sie nimmt mehrere Parameter entgegen:
xhr.open(method, url, async, user, password);method: Die zu verwendende HTTP-Methode, z. B.'GET'oder'POST'.url: Die URL, an die die Anfrage gesendet wird.async: Ein boolean, der angibt, ob die Anfrage asynchron ist. Standardmäßigtrue, und man sollte es fast immer so belassen (siehe Warnung unten).user: Optionaler Benutzername für die HTTP-Authentifizierung.password: Optionales Passwort für die HTTP-Authentifizierung.
Beispiel:
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);Synchrone Anfragen (xhr.open(method, url, false)) frieren die Seite ein, bis die Antwort eintrifft, und sind im Haupt-Thread veraltet. async sollte immer auf true gesetzt bleiben.
Die send-Methode
send sendet die Anfrage an den Server. Alle Event-Handler müssen vor dem Aufruf angehängt werden. Bei einer GET-Anfrage wird sie ohne Argumente aufgerufen. Bei einem POST wird der Anfrage-Body als Argument übergeben.
Beispiel einer GET-Anfrage:
xhr.send();Beispiel einer POST-Anfrage mit form-kodierten Daten:
xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xhr.send('param1=value1¶m2=value2');Die Methode setRequestHeader fügt der ausgehenden Anfrage einen HTTP-Header hinzu und muss nach open, aber vor send aufgerufen werden.
Server-Antworten verarbeiten
Zur Verarbeitung von Server-Antworten können verschiedene Event-Listener verwendet werden.
Das onreadystatechange-Event
Das onreadystatechange-Event wird immer dann ausgelöst, wenn sich die readyState-Eigenschaft ändert. Die readyState-Eigenschaft enthält den Status des XMLHttpRequest.
0: UNSENT1: OPENED2: HEADERS_RECEIVED3: LOADING4: DONE
Eine Anfrage ist nur dann abgeschlossen und erfolgreich, wenn readyState den Wert 4 (DONE) hat und der HTTP-status im Erfolgsbereich liegt (typischerweise 200). Nur auf readyState === 4 zu prüfen ist ein häufiger Fehler, da der Server mit 404 oder 500 geantwortet haben könnte.
Beispiel:
Obwohl onreadystatechange funktioniert, bevorzugt moderner Code im Allgemeinen onload und onerror für eine einfachere und besser lesbare Anfragenverarbeitung. onreadystatechange wird hauptsächlich verwendet, wenn Zwischenzustände verfolgt werden müssen (wie Fortschritt oder empfangene Header).
Das load-Event
Das load-Event wird ausgelöst, sobald die Antwort vollständig eingegangen ist. Es ist einfacher als onreadystatechange, da man readyState nicht selbst prüfen muss – es wird nur in der DONE-Phase ausgelöst. Man prüft weiterhin status, um einen echten Erfolg von einem HTTP-Fehler zu unterscheiden.
Beispiel:
Das progress-Event
Bei großen Downloads kann der Fortschritt mit dem progress-Event gemeldet werden. Wenn der Server einen Content-Length-Header sendet, ist das Event determiniert (lengthComputable ist true) und ein Prozentsatz kann berechnet werden:
xhr.onprogress = function(event) {
if (event.lengthComputable) {
const percent = Math.round((event.loaded / event.total) * 100);
console.log(`Downloaded ${percent}%`);
}
};Um stattdessen einen Upload zu verfolgen, werden Handler an xhr.upload angehängt (xhr.upload.onprogress). Das Upload-Fortschrittsobjekt ist die eine Funktion, die Fetch noch nicht vollständig replizieren kann.
Fehlerbehandlung
Robuster Code muss Fehler behandeln. Zwei Events decken die Fehlerfälle ab:
onerrorwird bei einem Fehler auf Netzwerkebene ausgelöst – die Anfrage hat den Server nie erreicht, DNS ist fehlgeschlagen, CORS hat sie blockiert usw. Beachte, dass ein HTTP-404oder500kein Netzwerkfehler ist: Er löstloadaus, nichterror– daher mussstatusweiterhin geprüft werden.ontimeoutwird ausgelöst, wenn die Anfrage länger alsxhr.timeoutMillisekunden dauert. Ein Timeout von0(der Standard) bedeutet kein Limit.
Beispiel:
JSON-Antworten parsen
Server-Antworten sind meistens JSON. Der einfachste Ansatz ist, den Rohtext aus xhr.responseText zu lesen und ihn selbst mit JSON.parse zu parsen:
Alternativ kann xhr.responseType = 'json' vor dem Senden gesetzt werden, und der Browser parst den Body automatisch. Der geparste Wert ist dann über xhr.response verfügbar (nicht xhr.responseText):
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts/1', true);
xhr.responseType = 'json';
xhr.onload = function() {
if (xhr.status === 200) {
console.log('title: ' + xhr.response.title); // already an object
}
};
xhr.send();responseType akzeptiert auch 'text', 'blob', 'arraybuffer' und 'document' für Nicht-JSON-Nutzdaten.
Daten mit POST senden
Um einen JSON-Body zu senden, wird der Content-Type-Header gesetzt und das Objekt mit JSON.stringify serialisiert:
const xhr = new XMLHttpRequest();
xhr.open('POST', 'https://jsonplaceholder.typicode.com/posts', true);
xhr.setRequestHeader('Content-Type', 'application/json');
xhr.onload = function() {
if (xhr.status === 201) { // 201 Created
console.log('Created:', xhr.responseText);
}
};
xhr.send(JSON.stringify({ title: 'foo', body: 'bar', userId: 1 }));Für herkömmliche Formularübermittlungen wird stattdessen ein FormData-Objekt gesendet – der Browser setzt den korrekten Multipart-Content-Type automatisch, daher sollte setRequestHeader dafür nicht aufgerufen werden.
Eine Anfrage abbrechen
xhr.abort() abbricht eine laufende Anfrage, zum Beispiel wenn der Benutzer die Seite verlässt oder eine neue Suchanfrage eingibt. Nach dem Abbruch wird das abort-Event anstelle von load ausgelöst:
const xhr = new XMLHttpRequest();
xhr.open('GET', 'https://jsonplaceholder.typicode.com/posts', true);
xhr.onabort = () => console.log('Request was cancelled');
xhr.send();
// Later, cancel it:
xhr.abort();Das Fetch-Äquivalent verwendet einen AbortController.
XMLHttpRequest vs. Fetch
| XMLHttpRequest | Fetch | |
|---|---|---|
| Programmiermodell | Callbacks / Events | Promises, funktioniert mit await |
| Upload-Fortschritt | Ja (xhr.upload) | Nein |
| Download-Fortschritt | Ja (progress-Event) | Über Streams (mehr Code) |
| Abbrechen | xhr.abort() | AbortController |
| Fehler bei HTTP-Fehler | Nein, status wird geprüft | Nein, response.ok wird geprüft |
Für die meisten neuen Projekte ist Fetch die bessere Wahl. Auf XHR sollte man zurückgreifen, wenn detaillierte Upload-Fortschrittsberichte benötigt werden oder eine sehr alte Umgebung unterstützt werden muss.
Fazit
XMLHttpRequest ermöglicht es JavaScript, im Hintergrund Daten mit einem Server auszutauschen: Man erstellt das Objekt, opent es, hängt load/error/timeout-Handler an und sendet es. Wichtig ist, sowohl readyState als auch status zu prüfen, JSON selbst oder über responseType zu parsen und abort() zu verwenden, um veraltete Anfragen abzubrechen. Für die meisten neuen Projekte ist die promise-basierte Fetch API die bessere Wahl, aber das Verständnis von XHR hält einen in älteren Codebasen und in Szenarien wie dem Upload-Fortschritt, wo XHR noch immer überlegen ist, handlungsfähig.