request(options)
Beschreibung
Führt XHR- (auch bekannt als AJAX-) Anfragen aus und gibt ein Promise zurück.
m.request({
method: 'PUT',
url: '/api/v1/users/:id',
params: { id: 1 },
body: { name: 'test' },
}).then(function (result) {
console.log(result);
});
Signatur
promise = m.request(options)
Argument | Type | Required | Description |
---|---|---|---|
options | Object | Yes | Die Anfrageoptionen, die übergeben werden. |
options.method | String | No | Die zu verwendende HTTP-Methode. Gültige Werte sind: GET , POST , PUT , PATCH , DELETE , HEAD oder OPTIONS . Standardwert ist GET . |
options.url | String | Yes | Der Pfadname, an den die Anfrage gesendet werden soll. Optional können Werte aus options.params in die URL interpoliert werden. |
options.params | Object | No | Die Daten, die in die URL interpoliert und/oder als Query-String serialisiert werden sollen. |
options.body | Object | No | Die Daten, die im Anfragekörper serialisiert werden sollen (für andere Anfragearten als GET). |
options.async | Boolean | No | Gibt an, ob die Anfrage asynchron erfolgen soll. Standardwert ist true . |
options.user | String | No | Ein Benutzername für die HTTP-Authentifizierung. Standardwert ist undefined . |
options.password | String | No | Ein Passwort für die HTTP-Authentifizierung. Standardwert ist undefined . Diese Option dient der XMLHttpRequest -Kompatibilität, sollte aber vermieden werden, da sie das Passwort im Klartext über das Netzwerk sendet. |
options.withCredentials | Boolean | No | Gibt an, ob Cookies an Drittanbieter-Domains gesendet werden sollen. Standardwert ist false . |
options.timeout | Number | No | Die maximale Dauer der Anfrage in Millisekunden, bevor sie automatisch beendet wird. Standardwert ist undefined . |
options.responseType | String | No | Der erwartete Typ der Antwort. Standardwert ist "" , wenn extract definiert ist, ansonsten "json" . Wenn responseType: "json" , wird intern JSON.parse(responseText) ausgeführt. |
options.config | xhr = Function(xhr) | No | Ermöglicht den Zugriff auf das zugrunde liegende XMLHttpRequest-Objekt für grundlegende Konfigurationen und optionalen Austausch (durch Rückgabe eines neuen XHR-Objekts). |
options.headers | Object | No | Header, die der Anfrage vor dem Senden hinzugefügt werden sollen (werden direkt vor options.config angewendet). |
options.type | any = Function(any) | No | Ein Konstruktor, der auf jedes Objekt in der Antwort angewendet werden soll. Standardwert ist die Identitätsfunktion. |
options.serialize | string = Function(any) | No | Eine Serialisierungsmethode, die auf body angewendet werden soll. Standardwert ist JSON.stringify oder, falls options.body eine Instanz von FormData oder URLSearchParams ist, die Identitätsfunktion (d. h. function(value) {return value} ). |
options.deserialize | any = Function(any) | No | Eine Deserialisierungsmethode, die auf xhr.response oder normalisiertes xhr.responseText angewendet werden soll. Standardwert ist die Identitätsfunktion. Wenn extract definiert ist, wird deserialize übersprungen. |
options.extract | any = Function(xhr, options) | No | Ein Hook, um festzulegen, wie die XMLHttpRequest-Antwort interpretiert werden soll. Nützlich für die Verarbeitung von Antwortdaten sowie zum Lesen von Headern und Cookies. Standardmäßig ist dies eine Funktion, die options.deserialize(parsedResponse) zurückgibt und eine Ausnahme auslöst, wenn der Antwortstatuscode des Servers einen Fehler signalisiert oder die Antwort syntaktisch ungültig ist. Wenn ein benutzerdefinierter Extraktions-Callback bereitgestellt wird, ist der xhr -Parameter die XMLHttpRequest-Instanz, die für die Anfrage verwendet wurde, und options das Objekt, das an den m.request -Aufruf übergeben wurde. Darüber hinaus wird deserialize übersprungen, und der vom Extraktions-Callback zurückgegebene Wert wird bei Auflösung des Promises unverändert übernommen. |
options.background | Boolean | No | Wenn false , werden gemountete Komponenten nach Abschluss der Anfrage neu gezeichnet. Wenn true , nicht. Standardwert ist false . |
returns | Promise | Ein Promise, das mit den Antwortdaten aufgelöst wird, nachdem diese durch die Methoden extract , deserialize und type verarbeitet wurden. Wenn der Antwortstatuscode einen Fehler anzeigt, wird das Promise abgewiesen. Dies kann jedoch durch Festlegen der Option extract verhindert werden. |
promise = m.request(url, options)
Argument | Type | Required | Description |
---|---|---|---|
url | String | Yes | Der Pfadname, an den die Anfrage gesendet werden soll. options.url überschreibt diesen Wert, falls vorhanden. |
options | Object | No | Die Anfrageoptionen, die übergeben werden. |
returns | Promise | Ein Promise, das mit den Antwortdaten aufgelöst wird, nachdem diese durch die Methoden extract , deserialize und type verarbeitet wurden. |
Diese zweite Form ist im Wesentlichen äquivalent zu m.request(Object.assign({url: url}, options))
, nur dass sie intern nicht von der globalen ES6-Funktion Object.assign
abhängt.
Funktionsweise
Das Hilfsprogramm m.request
ist eine schlanke Abstraktion über XMLHttpRequest
und ermöglicht das Senden von HTTP-Anfragen an Remote-Server, um Daten zu speichern und/oder abzurufen.
m.request({
method: 'GET',
url: '/api/v1/users',
}).then(function (users) {
console.log(users);
});
Ein Aufruf von m.request
gibt ein Promise zurück und löst nach Abschluss der Promise-Kette eine Neudarstellung aus.
Standardmäßig geht m.request
davon aus, dass die Antwort im JSON-Format vorliegt und parst sie in ein JavaScript-Objekt (oder Array).
Wenn der HTTP-Antwortstatuscode einen Fehler signalisiert, wird das zurückgegebene Promise abgewiesen. Die Bereitstellung eines Extract-Callbacks verhindert die Abweisung des Promises.
Typische Verwendung
Hier ist ein Beispiel einer Komponente, die m.request
verwendet, um Daten von einem Server abzurufen.
var Data = {
todos: {
list: [],
fetch: function () {
m.request({
method: 'GET',
url: '/api/v1/todos',
}).then(function (items) {
Data.todos.list = items;
});
},
},
};
var Todos = {
oninit: Data.todos.fetch,
view: function (vnode) {
return Data.todos.list.map(function (item) {
return m('div', item.title);
});
},
};
m.route(document.body, '/', {
'/': Todos,
});
Nehmen wir an, dass eine Anfrage an die Server-URL /api/items
ein Array von Objekten im JSON-Format zurückgibt.
Wenn m.route
am Ende aufgerufen wird, wird die Komponente Todos
erstellt. oninit
wird aufgerufen, wodurch m.request
aufgerufen wird. Dies ruft asynchron ein Array von Objekten vom Server ab. "Asynchron" bedeutet, dass JavaScript weiterhin anderen Code ausführt, während es auf die Antwort vom Server wartet. In diesem Fall bedeutet dies, dass fetch
zurückkehrt und die Komponente mit dem ursprünglichen leeren Array als Data.todos.list
gerendert wird. Sobald die Anfrage an den Server abgeschlossen ist, wird das Array von Objekten items
Data.todos.list
zugewiesen und die Komponente wird erneut gerendert, wodurch eine Liste von <div>
s mit den Titeln der einzelnen todo
s erzeugt wird.
Fehlerbehandlung
Wenn eine Nicht-file:
-Anfrage mit einem anderen Status als 2xx oder 304 zurückkehrt, wird sie mit einem Fehler abgewiesen. Dieser Fehler ist eine normale Error-Instanz, jedoch mit einigen speziellen Eigenschaften.
error.message
enthält den rohen Antworttext.error.code
enthält den Statuscode selbst.error.response
enthält die geparste Antwort, wobeioptions.extract
undoptions.deserialize
wie bei normalen Antworten verwendet werden.
Dies ist in vielen Fällen nützlich, in denen Fehler selbst Informationen enthalten, die Sie berücksichtigen können. Wenn Sie feststellen möchten, ob eine Sitzung abgelaufen ist, können Sie if (error.code === 401) return promptForAuth().then(retry)
ausführen. Wenn Sie auf den Drosselungsmechanismus einer API stoßen und diese einen Fehler mit einem "timeout": 1000
zurückgibt, können Sie ein setTimeout(retry, error.response.timeout)
ausführen.
Ladeicons und Fehlermeldungen
Hier ist eine erweiterte Version des vorherigen Beispiels, die eine Ladeanzeige und eine Fehlermeldung implementiert:
var Data = {
todos: {
list: null,
error: '',
fetch: function () {
m.request({
method: 'GET',
url: '/api/v1/todos',
})
.then(function (items) {
Data.todos.list = items;
})
.catch(function (e) {
Data.todos.error = e.message;
});
},
},
};
var Todos = {
oninit: Data.todos.fetch,
view: function (vnode) {
return Data.todos.error
? [m('.error', Data.todos.error)]
: Data.todos.list
? [
Data.todos.list.map(function (item) {
return m('div', item.title);
}),
]
: m('.loading-icon');
},
};
m.route(document.body, '/', {
'/': Todos,
});
Es gibt einige Unterschiede zwischen diesem Beispiel und dem vorherigen. Hier ist Data.todos.list
am Anfang null
. Außerdem gibt es ein zusätzliches Feld error
zum Speichern einer Fehlermeldung, und die Ansicht der Komponente Todos
wurde geändert, um eine Fehlermeldung anzuzeigen, falls eine vorhanden ist, oder ein Ladeicon anzuzeigen, falls Data.todos.list
kein Array ist.
Dynamische URLs
Anfrage-URLs können Interpolationen enthalten:
m.request({
method: 'GET',
url: '/api/v1/users/:id',
params: { id: 123 },
}).then(function (user) {
console.log(user.id); // logs 123
});
Im obigen Code wird :id
durch die Daten aus dem params
-Objekt ersetzt, wodurch die Anfrage zu GET /api/v1/users/123
wird.
Interpolationen werden ignoriert, wenn keine übereinstimmenden Daten in der Eigenschaft params
vorhanden sind.
m.request({
method: 'GET',
url: '/api/v1/users/foo:bar',
params: { id: 123 },
});
Im obigen Code wird die Anfrage zu GET /api/v1/users/foo:bar?id=123
Abbrechen von Anfragen
Manchmal ist es wünschenswert, eine Anfrage abzubrechen. Beispielsweise möchten Sie in einem Autocompleter-/Typeahead-Widget sicherstellen, dass nur die letzte Anfrage abgeschlossen wird, da Autocompleter in der Regel mehrere Anfragen auslösen, während der Benutzer tippt, und HTTP-Anfragen aufgrund der unvorhersehbaren Natur von Netzwerken möglicherweise nicht in der richtigen Reihenfolge abgeschlossen werden. Wenn eine andere Anfrage nach der zuletzt gesendeten Anfrage abgeschlossen wird, würde das Widget weniger relevante (oder potenziell falsche) Daten anzeigen, als wenn die zuletzt gesendete Anfrage als letzte abgeschlossen würde.
m.request()
legt sein zugrunde liegendes XMLHttpRequest
-Objekt über den Parameter options.config
offen, wodurch Sie einen Verweis auf dieses Objekt speichern und bei Bedarf seine abort
-Methode aufrufen können:
var searchXHR = null;
function search() {
abortPreviousSearch();
m.request({
method: 'GET',
url: '/api/v1/users',
params: { search: query },
config: function (xhr) {
searchXHR = xhr;
},
});
}
function abortPreviousSearch() {
if (searchXHR !== null) searchXHR.abort();
searchXHR = null;
}
Datei-Uploads
Um Dateien hochzuladen, müssen Sie zuerst einen Verweis auf ein File
-Objekt erhalten. Der einfachste Weg, dies zu tun, ist über ein <input type="file">
.
m.render(document.body, [m('input[type=file]', { onchange: upload })]);
function upload(e) {
var file = e.target.files[0];
}
Der obige Code rendert ein Dateieingabefeld. Wenn ein Benutzer eine Datei auswählt, wird das onchange
-Ereignis ausgelöst, das die Funktion upload
aufruft. e.target.files
ist eine Liste von File
-Objekten.
Als Nächstes müssen Sie ein FormData
-Objekt erstellen, um eine mehrteilige Anfrage (Multipart-Anfrage) zu erstellen. Dies ist eine speziell formatierte HTTP-Anfrage, mit der Dateidaten im Anfragetext übertragen werden können.
function upload(e) {
var file = e.target.files[0];
var body = new FormData();
body.append('myfile', file);
}
Als Nächstes müssen Sie m.request
aufrufen und options.method
auf eine HTTP-Methode setzen, die einen Body verwendet (z. B. POST
, PUT
, PATCH
), und das FormData
-Objekt als options.body
verwenden.
function upload(e) {
var file = e.target.files[0];
var body = new FormData();
body.append('myfile', file);
m.request({
method: 'POST',
url: '/api/v1/upload',
body: body,
});
}
Vorausgesetzt, der Server ist für die Annahme von Multipart-Anfragen konfiguriert, werden die Dateiinformationen dem Schlüssel myfile
zugeordnet.
Mehrere Datei-Uploads
Es ist möglich, mehrere Dateien in einer Anfrage hochzuladen. Dadurch wird der Stapel-Upload (Batch-Upload) atomar, d. h. es werden keine Dateien verarbeitet, wenn während des Uploads ein Fehler auftritt, sodass es nicht möglich ist, nur einen Teil der Dateien zu speichern. Wenn Sie im Falle eines Netzwerkausfalls so viele Dateien wie möglich speichern möchten, sollten Sie stattdessen erwägen, jede Datei in einer separaten Anfrage hochzuladen.
Um mehrere Dateien hochzuladen, hängen Sie sie einfach alle an das FormData
-Objekt an. Wenn Sie ein Dateieingabefeld verwenden, können Sie eine Liste von Dateien abrufen, indem Sie dem Eingabefeld das Attribut multiple
hinzufügen:
m.render(document.body, [
m('input[type=file][multiple]', { onchange: upload }),
]);
function upload(e) {
var files = e.target.files;
var body = new FormData();
for (var i = 0; i < files.length; i++) {
body.append('file' + i, files[i]);
}
m.request({
method: 'POST',
url: '/api/v1/upload',
body: body,
});
}
Fortschritt überwachen
Manchmal ist es wünschenswert, dem Benutzer eine Fortschrittsanzeige anzuzeigen, um zu signalisieren, dass die Anwendung noch funktioniert, wenn eine Anfrage von Natur aus langsam ist (z. B. ein großer Datei-Upload).
m.request()
legt sein zugrunde liegendes XMLHttpRequest
-Objekt über den Parameter options.config
offen, wodurch Sie Fortschrittsüberwacher (Fortschrittslistener) an das XMLHttpRequest-Objekt anhängen können:
var progress = 0;
m.mount(document.body, {
view: function () {
return [
m('input[type=file]', { onchange: upload }),
progress + '% completed',
];
},
});
function upload(e) {
var file = e.target.files[0];
var body = new FormData();
body.append('myfile', file);
m.request({
method: 'POST',
url: '/api/v1/upload',
body: body,
config: function (xhr) {
xhr.upload.addEventListener('progress', function (e) {
progress = e.loaded / e.total;
m.redraw(); // tell Mithril.js that data changed and a re-render is needed
});
},
});
}
Im obigen Beispiel wird ein Dateieingabefeld gerendert. Wenn der Benutzer eine Datei auswählt, wird ein Upload initiiert und im config
-Callback wird ein progress
-Ereignishandler registriert. Dieser Ereignishandler wird ausgelöst, wenn es ein Fortschrittsupdate in der XMLHttpRequest gibt. Da das Fortschrittsereignis der XMLHttpRequest nicht direkt von der Virtual-DOM-Engine von Mithril.js verarbeitet wird, muss m.redraw()
aufgerufen werden, um Mithril.js zu signalisieren, dass sich Daten geändert haben und eine Neudarstellung erforderlich ist.
Antwort in einen Typ umwandeln
Abhängig von der Gesamtarchitektur der Anwendung kann es wünschenswert sein, die Antwortdaten einer Anfrage in eine bestimmte Klasse oder einen bestimmten Typ zu transformieren (z. B. um Datumsfelder einheitlich zu parsen oder um Hilfsmethoden zu haben).
Sie können einen Konstruktor als Parameter options.type
übergeben, und Mithril.js instanziiert ihn für jedes Objekt in der HTTP-Antwort.
function User(data) {
this.name = data.firstName + ' ' + data.lastName;
}
m.request({
method: 'GET',
url: '/api/v1/users',
type: User,
}).then(function (users) {
console.log(users[0].name); // logs a name
});
Im obigen Beispiel wird davon ausgegangen, dass /api/v1/users
ein Array von Objekten zurückgibt, der Konstruktor User
für jedes Objekt im Array instanziiert wird (d. h. als new User(data)
aufgerufen wird). Wenn die Antwort ein einzelnes Objekt zurückgibt, wird dieses Objekt als body
-Argument verwendet.
Nicht-JSON-Antworten
Manchmal gibt ein Server-Endpunkt keine JSON-Antwort zurück: Beispielsweise fordern Sie möglicherweise eine HTML-Datei, eine SVG-Datei oder eine CSV-Datei an. Standardmäßig versucht Mithril.js, eine Antwort so zu parsen, als wäre sie JSON. Um dieses Verhalten zu überschreiben, definieren Sie eine benutzerdefinierte Funktion options.deserialize
:
m.request({
method: 'GET',
url: '/files/icon.svg',
deserialize: function (value) {
return value;
},
}).then(function (svg) {
m.render(document.body, m.trust(svg));
});
Im obigen Beispiel ruft die Anfrage eine SVG-Datei ab, unternimmt nichts, um sie zu parsen (da deserialize
lediglich den Wert unverändert zurückgibt), und rendert dann die SVG-Zeichenfolge als vertrauenswürdiges HTML.
Natürlich kann eine deserialize
-Funktion auch aufwändiger sein:
m.request({
method: 'GET',
url: '/files/data.csv',
deserialize: parseCSV,
}).then(function (data) {
console.log(data);
});
function parseCSV(data) {
// naive implementation for the sake of keeping example simple
return data.split('\n').map(function (row) {
return row.split(',');
});
}
Abgesehen von der Tatsache, dass die obige parseCSV-Funktion nicht viele Fälle behandelt, die ein ordnungsgemäßer CSV-Parser behandeln würde, protokolliert der obige Code ein Array von Arrays.
Benutzerdefinierte Header können in dieser Hinsicht ebenfalls hilfreich sein. Wenn Sie beispielsweise eine SVG-Datei anfordern, möchten Sie wahrscheinlich den Inhaltstyp entsprechend festlegen. Um den Standard-JSON-Anfragetyp zu überschreiben, setzen Sie options.headers
auf ein Objekt mit Schlüssel-Wert-Paaren, die den Namen und Werten der Anfrageheader entsprechen.
m.request({
method: 'GET',
url: '/files/image.svg',
headers: {
'Content-Type': 'image/svg+xml; charset=utf-8',
Accept: 'image/svg, text/*',
},
deserialize: function (value) {
return value;
},
});
Abrufen von Antwortdetails
Standardmäßig versucht Mithril.js, xhr.responseText
als JSON zu parsen und gibt das geparste Objekt zurück. Es kann nützlich sein, eine Serverantwort detaillierter zu untersuchen und manuell zu verarbeiten. Dies kann erreicht werden, indem eine benutzerdefinierte Funktion options.extract
übergeben wird:
m.request({
method: 'GET',
url: '/api/v1/users',
extract: function (xhr) {
return { status: xhr.status, body: xhr.responseText };
},
}).then(function (response) {
console.log(response.status, response.body);
});
Der Parameter für options.extract
ist das XMLHttpRequest-Objekt, sobald der Vorgang abgeschlossen ist, aber bevor es an die zurückgegebene Promise-Kette übergeben wurde, sodass das Promise immer noch in einem abgelehnten Zustand enden kann, wenn die Verarbeitung eine Ausnahme auslöst.
Ausgeben von Fetches an IP-Adressen
Aufgrund der (sehr simplen) Art und Weise, wie Parameter in URLs erkannt werden, werden IPv6-Adresssegmente als Pfadparameter-Interpolationen verwechselt, und da Pfadparameter etwas benötigen, um sie zu trennen, um korrekt interpoliert zu werden, führt dies zu einem Fehler.
// This doesn't work
m.request('http://[2001:db8::990a:cd27:4d9e:79]:8080/some/path', {
// ...
});
Um dies zu umgehen, sollten Sie das IPv6-Adress- + Port-Paar stattdessen als Parameter übergeben.
m.request('http://:host/some/path', {
params: { host: '[2001:db8::990a:cd27:4d9e:79]:8080' },
// ...
});
Dies ist kein Problem bei IPv4-Adressen, und Sie können diese normal verwenden.
// This will work as you expect
m.request('http://192.0.2.15:8080/some/path', {
// ...
});
Warum JSON anstelle von HTML
Viele serverseitige Frameworks bieten eine View-Engine, die Datenbankdaten in eine Vorlage interpoliert, bevor HTML bereitgestellt wird (beim Laden der Seite oder über AJAX), und verwenden dann jQuery, um Benutzerinteraktionen zu verarbeiten.
Im Gegensatz dazu ist Mithril.js ein Framework, das für Anwendungen mit umfangreicher Client-Logik entwickelt wurde, die in der Regel Vorlagen und Daten separat herunterladen und sie im Browser über JavaScript kombinieren. Die Verlagerung der Templating-Schwerarbeit in den Browser kann Vorteile bringen, wie z. B. die Reduzierung der Betriebskosten durch die Freigabe von Serverressourcen. Die Trennung von Vorlagen und Daten ermöglicht auch eine effektivere Zwischenspeicherung von Vorlagencode und ermöglicht eine bessere Wiederverwendbarkeit von Code über verschiedene Arten von Clients hinweg (z. B. Desktop, Mobil). Ein weiterer Vorteil ist, dass Mithril.js ein Retained-Mode-Paradigma für die UI-Entwicklung ermöglicht, das die Entwicklung und Wartung komplexer Benutzerinteraktionen erheblich vereinfacht.
Standardmäßig erwartet m.request
, dass die Antwortdaten im JSON-Format vorliegen. In einer typischen Mithril.js-Anwendung werden diese JSON-Daten dann normalerweise von einer View konsumiert.
Sie sollten vermeiden, zu versuchen, serverseitig generiertes dynamisches HTML mit Mithril zu rendern. Wenn Sie eine vorhandene Anwendung haben, die ein serverseitiges Templating-System verwendet, und Sie diese neu strukturieren möchten, entscheiden Sie zunächst, ob der Aufwand überhaupt machbar ist. Die Migration von einer Thick-Server-Architektur zu einer Architektur mit umfangreicher Client-Logik ist in der Regel ein etwas großer Aufwand und beinhaltet die Refaktorierung von Logik aus Vorlagen in logische Datendienste (und die damit verbundenen Tests).
Datendienste können je nach Art der Anwendung auf viele verschiedene Arten organisiert werden. RESTful-Architekturen sind bei API-Anbietern beliebt, und Serviceorientierte Architekturen sind oft erforderlich, wenn es viele hochtransaktionale Workflows gibt.
Warum XHR anstelle von Fetch
fetch()
ist eine neuere Web-API zum Abrufen von Ressourcen von Servern, ähnlich wie XMLHttpRequest
.
Mithril.js' m.request
verwendet XMLHttpRequest
anstelle von fetch()
aus einer Reihe von Gründen:
fetch
ist noch nicht vollständig standardisiert und kann Spezifikationsänderungen unterliegen.XMLHttpRequest
-Aufrufe können abgebrochen werden, bevor sie aufgelöst werden (z. B. um Wettlaufsituationen in Instant-Search-UIs zu vermeiden).XMLHttpRequest
bietet Hooks für Fortschrittsüberwacher (Fortschrittslistener) für lang andauernde Anfragen (z. B. Datei-Uploads).XMLHttpRequest
wird von allen Browsern unterstützt, währendfetch()
nicht von Internet Explorer und älteren Android-Versionen (vor 5.0 Lollipop) unterstützt wird.
Derzeit erfordert fetch()
aufgrund mangelnder Browserunterstützung in der Regel eine Standardvorlage (Polyfill), die unkomprimiert über 11 KB groß ist - fast dreimal größer als das XHR-Modul von Mithril.js.
Obwohl es viel kleiner ist, unterstützt das XHR-Modul von Mithril.js viele wichtige und nicht so trivial zu implementierende Funktionen wie URL-Interpolation und Serialisierung von Abfragezeichenketten (Querystring-Serialisierung) zusätzlich zu seiner Fähigkeit, sich nahtlos in das automatische Neuzeichnen-Subsystem von Mithril.js zu integrieren. Die fetch
-Standardvorlage (Polyfill) unterstützt keine dieser Funktionen und erfordert zusätzliche Bibliotheken und Wiederholungscode (Boilerplates), um das gleiche Maß an Funktionalität zu erreichen.
Darüber hinaus ist das XHR-Modul von Mithril.js für JSON-basierte Endpunkte optimiert und macht diesen häufigsten Fall entsprechend prägnant - d. h. m.request(url)
- während fetch
einen zusätzlichen expliziten Schritt erfordert, um die Antwortdaten als JSON zu parsen: fetch(url).then(function(response) {return response.json()})
Die fetch()
-API hat in einigen ungewöhnlichen Fällen einige technische Vorteile gegenüber XMLHttpRequest
:
- Sie bietet eine Streaming-API (im Sinne von "Video-Streaming", nicht im Sinne von reaktiver Programmierung), die eine bessere Latenz und einen besseren Speicherverbrauch für sehr große Antworten ermöglicht (auf Kosten der Codekomplexität).
- Sie integriert sich in die Service Worker API, die eine zusätzliche Kontrollebene darüber bietet, wie und wann Netzwerkanfragen erfolgen. Diese API ermöglicht auch den Zugriff auf Push-Benachrichtigungen und Hintergrundsynchronisierungsfunktionen.
In typischen Szenarien bietet Streaming keine spürbaren Leistungsvorteile, da es im Allgemeinen nicht ratsam ist, überhaupt Megabyte an Daten herunterzuladen. Auch die Speichergewinne durch die wiederholte Wiederverwendung kleiner Puffer können ausgeglichen oder zunichte gemacht werden, wenn sie zu übermäßigen Browser-Repaints führen. Aus diesen Gründen wird die Wahl von fetch()
-Streaming anstelle von m.request
nur für extrem ressourcenintensive Anwendungen empfohlen.
Anti-Patterns vermeiden
Promises sind nicht die Antwortdaten
Die Methode m.request
gibt ein Promise zurück, nicht die Antwortdaten selbst. Sie kann diese Daten nicht direkt zurückgeben, da eine HTTP-Anfrage lange dauern kann (aufgrund der Netzwerklatenz), und wenn JavaScript darauf warten würde, würde es die Anwendung einfrieren, bis die Daten verfügbar sind.
// VERMEIDEN SIE
var users = m.request('/api/v1/users');
console.log('list of users:', users);
// `users` ist KEINE Liste von Benutzern, sondern ein Promise
// ZIEHEN SIE VOR
m.request('/api/v1/users').then(function (users) {
console.log('list of users:', users);
});