Deine AJAX-Requests sind unübersichtlich und verwinkelt?
Benutze Promises, um Deine Anfragen simpler zu machen und den Urwald aufzuräumen!
AJAX
-Requests
AJAX
steht für Asynchronous JavaScript and XML.
Asynchron bedeutet, dass Inhalte (z.B. Bilder oderJSON
-Dateien) nach dem eigentlichen Laden der Seite mit Hilfe von JavaScript nachgeladen werden können. Das macht Webseiten dynamischer.
Mehr Infos zuAJAX
findest du im MDN.
Promises
Ein Promise ist ein Objekt, das eine Verbindung zwischen einem Request und den dazugehörigen Event-Handlern herstellt. Es verspricht und stellt sicher, dass bestimmte Funktionen ausgeführt werden.
Ein Promise hat immer einen dieser Status:
pending
wenn eine Antwort noch ausstehtfulfilled
wenn der Request erfolgreich warrejected
wenn der Request fehlschlug
Der Struggle mit den Events ist real
„Kann man das nicht auch mit Events lösen?“
Ja, aber lange nicht so gut.
Während Events für immer wieder auftretende Ereignisse wie keydown
oder click
gut geeignet sind, sind sie bei asynchronen Requests schwer unter Kontrolle zu bekommen, denn man weiß nie genau, wann und wie oft der Event-Handler aufgerufen wird.
Event-Handler
Event-Handler sind JavaScript-Funktionen, die ausgeführt werden, wenn ein bestimmtes Ereignis (wie z.B. das oben erwähnteclick
-Ereignis) an einem Element auftritt.
Lerne mehr über Event-Handler bei selfhtml.
Lösung: Die fetch
-API
Wo man früher noch mit XMLHttpRequest
herumgeeiert hat, gibt es inzwischen weitaus elegantere Lösungen.
Einen einfachen Weg bietet die fetch
-API. Sie ist in den meisten aktuellen Browsern in Form von window.fetch
bereits nativ vorhanden.
Um sie in allen Browsern nutzen zu können, empfiehlt es sich, diese Funktion über einen Polyfill nachzurüsten.
z.B. von GitHub oder mit npm: npm install whatwg-fetch
Da noch nicht alle Browser Promises unterstützen, sollte auch hierfür ein Polyfill eingesetzt werden.
z.B. von GitHub oder mit npm: npm install promise-polyfill
Requests mit fetch
haben einen einfachen Aufbau:
- Aufruf der Funktion
fetch
mit der URL des Webservices
then
-Blöcke, die aufgerufen werden, wenn der Request erfolgreich war- Einen
catch
-Block, in dem festgelegt wird, was passiert, wenn der Request fehlschlug. Das kann beispielsweise eine Fehlermeldung oder ein Timeout sein.
fetch(url)
.then(function(response) {
/* Anwort hier verarbeiten */
})
.catch(function(error) {
/* beim Error kommt man hier raus */
})
Ein einfacher HTTP GET
-Request könnte wie folgt aussehen:
fetch('json/first.json')
.then(function(response) {
// erfülltes Promise-Objekt in JSON umwandeln
return response.json()
}).then(function(json) {
// Yes! Die Request war erfolgreich.
console.log(json)
}).catch(function(error) {
/* Verdammt. Es gab einen Fehler beim Laden oder beim Parsen des JSON. */
console.log('fetch failed: ', error)
})
Dem eifrigen Beobachter fällt schnell auf: Hier sind ja zwei then
-Blocks verbaut!
Die then
-Funktion kann beliebig oft hintereinander aufgerufen werden, so lange die im then
-Block davor übergebene Funktion einen Wert zurückgibt.
Es kann auch ein weiterer Promise zurückgegeben werden:
fetch('json/first.json').then(function(response) {
return response.json()
}).then(function(json) {
// 1. Request war erfolgreich
console.log(json)
// URL für 2. Request
var url = 'json/second.json'
// 2. Request wird gestartet und weitergegeben
return fetch(url)
}).then(function(response) {
return response.json()
}).then(function(json) {
// 2. Request war auch erfolgreich
console.log(json)
}).catch(function(ex){
// eine der Requests war nicht erfolgreich
console.log("error")
})
Dadurch wird der zweite Request erst abgefeuert, wenn der erste erfolgreich war. Wenn einer der beiden Requests erfolglos war, wird die catch
-Funktion ausgeführt.
Obacht!
So wie die Verpackung Deines letzten Fastfood-Burgers sind auch Promises nur für die einmalige Benutzung geeignet. Ist ein Promise einmalsettled
(also entwederfulfilled
oderrejected
), ist es verbraucht und ein neuer Promise muss erstellt werden.
Noch mehr Obacht!
Bei den HTTP-Responses mit den Codes404
(Not Found),500
(Internal Server Error) oder204
(No Content), die generell als Misserfolg gewertet werden können, wird in denthen
-Block gesprungen. Generell solltest du nur Responses mit dem Code200
annehmen.
Du kannst den Statuscode des Request im erstenthen
-Block mitresponse.status
prüfen.
Alle HTTP-Statuscodes findest du bei Wikipedia.
Advanced
Genau so wie mit response.status
kannst du auch weitere Parameter der Response wie z.B. den Statustext (response.statusText
) oder den Content-Type (response.headers.get('Content-Type')
) auslesen.
Du kannst der fetch
-Funktion noch weitere Parameter für den HTTP-Request in einem options
-Objekt übergeben übergeben: fetch( url, {headers: {'Content-Type': 'application/json'}} )
.
Auch andere HTTP-Methoden als GET
wie z.B. POST
oder PUT
sind möglich. Schau dir dazu am Besten die Dokumentation des fetch
-Polyfills auf GitHub an.
Quellen
https://developers.google.com/web/fundamentals/primers/promises
https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API
Alle Beispiele von oben findest du auch auf GitHub.