Goodbye XMLHttpRequest!
fetch()
allows you to make requests similar to XMLHttpRequest (XHR). The main difference is that the Fetch API uses
Promises (Promises) , which allow you to use a simpler and cleaner API, avoid a catastrophic number of callbacks and the need to remember the API for XMLHttpRequest.
The Fetch API is now available to users along with the
Service Workers in the global scoop in Chrome 40, but already in version 42 it will be available in the scooter window. Of course, for all
other browsers that do not yet support fetch, there is a
GitHub polyfill that is available today.
Simple Fetch Request
Let's start by comparing a simple example implemented with
XMLHttpRequest
and
fetch
. All that we will do in this example is to make a request for the URL, get the answer and parse it as JSON.
')
XMLHttpRequest
The example with XMLHttpRequest will require us to set two event handlers for
success
and
error
, as well as to call two methods:
open()
and
send()
.
Example from MDN documentation :
function reqListener() { var data = JSON.parse(this.responseText); console.log(data); } function reqError(err) { console.log('Fetch Error :-S', err); } var oReq = new XMLHttpRequest(); oReq.onload = reqListener; oReq.onerror = reqError; oReq.open('get', './api/some.json', true); oReq.send();
Fetch
Our fetch request will look like this:
fetch('./api/some.json') .then( function(response) { if (response.status !== 200) { console.log('Looks like there was a problem. Status Code: ' + response.status); return; }
First of all, we check the status of the response and check whether the request was successful (we expect 200 status). If everything is fine, then the parsim response is like JSON.
The
fetch()
response is a
Stream object . This means that as a result of calling the
json()
method, we get a Promise, because reading from such an object is asynchronous.
Response metadata
In the previous example, we learned how to check the status of the response object and to convention the response itself in JSON. The remaining metadata you can access (for example, headers) is listed below:
fetch('users.json').then(function(response) { console.log(response.headers.get('Content-Type')); console.log(response.headers.get('Date')); console.log(response.status); console.log(response.statusText); console.log(response.type); console.log(response.url); });
Types of response
When we make a fetch request, the response will be given the type "basic", "cors" or "opaque". These “types” indicate what resource the data came from and can be used to determine the data processing.
When a request is made to a resource located on the same origin (
meaning that the request is executed within the same site, approx. Lane ), the answer will contain the “basic” type and there will be no restrictions for such a request.
If the request is made from one origin to another (a cross-domain request), which, in turn, returned the
CORS headers, then the type will be “cors”. Objects with “cors” and “basic” types are almost identical, but “cors” somewhat limits the metadata that can be accessed before “Cache-Control”, “Content-Language”, “Content-Type”, “Expires”, “Last-Modified”, and “Pragma”.
As for the "opaque" - then it comes in cases when a CORS request is executed, but the remote resource does not return CORS headers. This type of request does not provide access to the data or status header, so we are not able to judge the result of the request. As part of the current implementation of
fetch()
it is not possible to execute CORS requests from the window's skoup, and
here it is written why. This functionality should be added as soon as the
Cache API is available from the window object.
You can define the expected query mode, thereby filtering the results of queries with the wrong type. Query mode can be set to the following:
- “same-origin” is successfully executed only for requests for the same origin, all other requests will be rejected.
- “cors” works in the same way as “same-origin” + adds the ability to create requests to third-party sites if they return the corresponding CORS headers.
- “cors-with-forced-preflight” works the same as “cors”, but always
sends a test request for verification before a request.
- “no-cors” is used when you need to make a request to origin, which does not send CORS headers and the result of execution is an object with the type “opaque”. As mentioned above, at the moment this is not possible in a window.
To determine the request mode, add an option object with the second parameter to the request and set “mode” in this object:
fetch('http://some-site.com/cors-enabled/some.json', {mode: 'cors'}) .then(function(response) { return response.text(); }) .then(function(text) { console.log('Request successful', text); }) .catch(function(error) { log('Request failed', error) });
Promises Chains
One of the great features of Promises is the ability to group them into chains. If we talk about them in
fetch()
, then they allow us to “fumble” the logic between requests.
If you are working with the JSON API, you will need to check the status and parse the JSON for each response. You can simplify your code by defining status parsing and JSON as separate functions that will be returned by promises. You will only think about processing the data itself and, of course, exceptions.
function status(response) { if (response.status >= 200 && response.status < 300) { return Promise.resolve(response) } else { return Promise.reject(new Error(response.statusText)) } } function json(response) { return response.json() } fetch('users.json') .then(status) .then(json) .then(function(data) { console.log('Request succeeded with JSON response', data); }).catch(function(error) { console.log('Request failed', error); });
We define a function that checks
response.status
and returns the result:
Promise.resolve()
or
Promise.reject()
. This is the first method called in our chain, and if it completes successfully (
Promise.resolve()
), the method following it is called
fetch()
, which, in turn, returns Promise from
response.json()
again. After this call, in case of successful execution, we will have a ready-made JSON object. If the parsing fails, Promise will be canceled and the condition for the exception will work.
But the best thing here is the ability to reuse such code for all fetch requests in the application. Such code is easier to maintain, read and test.
POST request
For a long time no one will be surprised by the need to use the POST method with passing parameters in the "body" of a request to work with the API.
To make such a request, we need to specify the appropriate parameters in the
fetch()
settings object:
fetch(url, { method: 'post', headers: { "Content-type": "application/x-www-form-urlencoded; charset=UTF-8" }, body: 'foo=bar&lorem=ipsum' }) .then(json) .then(function (data) { console.log('Request succeeded with JSON response', data); }) .catch(function (error) { console.log('Request failed', error); });
We send accounting data through Fetch-request
If you want to send a request with any credentials (for example, with a cookie), you should set `credentials` in the request options for“ include ”:
fetch(url, { credentials: 'include' })
FAQ
Can I cancel the
fetch()
request?
This is currently not possible, but it is being actively discussed on GitHub
Is there a polyfil?
YesWhy is “no-cors” implemented for service workers, but not for window?
This was done for security reasons. More details can be found
here .