A deeper look at fetch
Requesting resources from an API is a popular and nearly crucial feature required for building modern applications. Whether you have created your own API or you are implementing a third-party API, you need a way to create your requests without slowing down your application.
fetch() is an upgraded version of
XMLHttpRequest is that the Fetch API uses Promises, hence avoiding callback hell. The fetch API is natively supported by all modern browsers except Internet Explorer. This article details its usage. This is my 35th Medium article.
The function of
fetch() is basically the same as
XMLHttpRequest, but there are three main differences.
fetch()uses promise instead of the callback function, so it greatly simplifies the writing and makes writing more concise.
fetch()adopts modular design and the API is scattered across multiple objects (Response object, Request object, Headers object). By contrast, the API design of XMLHttpRequest is not very good — input, output, and status are all it has. It’s easy to write very messy code with the same interface management.
fetch()processes data through a data stream (Stream object), which can be read in blocks, which is beneficial to improve website performance and reduce memory usage. It’s very useful for scenarios where large files are requested or the network speed is slow. The
XMLHTTPRequestobject does not support data streaming. All data must be stored in the cache. Block reading is not supported. You must wait for all to be obtained before spitting it out in one go.
In terms of usage,
fetch() accepts a URL string as a parameter, sends a
GET request to the URL by default, and returns a
Promise object. Its basic usage is as follows:
Below is an example to get JSON data from the server:
In the above example, the
response received by
fetch() is a Stream object, and
response.json() is an asynchronous operation that takes out all the content and converts it into a JSON object. Promise can be rewritten using await syntax to make the semantics clearer.
In the above example, the
await statement must be placed inside the
try...catch, to catch errors that may occur in asynchronous operations. The following text uses the wording
await instead of of
Synchronous properties of the Response object
const response = await fetch(url);
As mentioned earlier, the data contained in
Response is read asynchronously through the
Stream interface, but it also contains some synchronous attributes, which correspond to the header information of the HTTP response (Headers), which can be read immediately.
In the above example,
response.statusText are the synchronous attributes of
Response and can be read immediately.
Response.ok property returns a boolean value, indicating whether the request is successful,
true corresponds to the HTTP request status code 200 to 299, and
false corresponds to other status codes.
Response.status property returns a number indicating the status code of the HTTP response (for example, 200, indicating a successful request).
Response.statusText property returns a string representing the status information of the HTTP response (for example, after the request is successful, the server returns “OK”).
Response.url property returns the requested URL. If the URL has a redirect, this attribute returns the final URL.
Response.type property returns the type of request. The possible values are as follows:
basic: Ordinary, same-origin request.
cors: Cross-origin request.
error: Network errors, mainly used for service workers.
opaque: If the
modeattribute of the
fetch()request is set to
no-cors, this response value will be returned.
opaqueredirect: If the
redirectattribute of the
fetch()request is set to
manual, this response value will be returned.
Response.redirected property returns a Boolean value, indicating whether the request has been redirected.
Determine whether the request is successful
fetch() sends a request, there is an important point to note:
fetch() will report an error only when there’s a network error or cannot connect. In other cases, no error will be reported, but the request is considered successful.
This means, even if the status code returned by the server is 4xx or 5xx,
fetch() will not report an error (i.e. The Promise will not become
rejected). Only by obtaining the true status code of the HTTP response through the
Responese.status property, can it be determined whether the request is successful. Please see the following example:
In the above example, the
Responese.status attribute must be equal to 2xx (200~299) to determine that the request is successful. There’s no need to consider the URL jump (status code is 3xx) because
fetch() will automatically convert the jumped status code to 200. Another method is to determine whether
Response object also has a
Responese.headers property, which points to a
Headers object, which corresponds to all the headers of the HTTP response.
Headers objects can be traversed using
Headers object provides the following methods to manipulate headers.
Headers.get(): According to the specified key name, return the key-value.
Headers.has(): Returns a Boolean value indicating whether a header is included.
Headers.set(): Set the specified key name as the new key-value, if the key name does not exist, it will be added.
Headers.append(): Add headers.
Headers.delete(): Delete the header.
Headers.keys(): Return an iterator that can traverse all the keys in turn.
Headers.values(): Return an iterator that can traverse all key values in turn.
Headers.entries(): Return an iterator that can traverse all key-value pairs in turn (
Headers.forEach(): Traverse the headers, in turn. Each header will execute a parameter function.
Some of the above methods can modify the headers because they inherit from the
Headers interface. For HTTP responses, modifying headers is of little significance — many headers are read-only and browsers do not allow modification. Among these methods, the most commonly used is
response.headers.get(), which is used to read the value of a certain header.
Headers.values() methods are used to traverse the header keys and key values respectively.
Headers.forEach() method can also traverse all key values and key names.
How to read content
Response object provides different reading methods according to different types of data returned by the server.
response.text(): Get the text string.
response.json(): Get the JSON object.
response.blob(): Get the binary
response.formData(): Get the
response.arrayBuffer(): Get the binary
The above five reading methods are all asynchronous and all return
Promise objects. You must wait until the end of the asynchronous operation to get the complete data returned by the server.
response.text() can be used to get text data, such as HTML files.
response.json() is mainly used to get the JSON data returned by the server. The example has been given earlier.
response.formData() is mainly used in Service Worker to intercept the form submitted by the user, modify some data, and then submit it to the server.
response.blob() is used to get the binary file.
The above example reads the
flower.jpg image file and displays it on the web page.
response.arrayBuffer() is mainly used to obtain streaming media files.
The above example is an example where
response.arrayBuffer() gets the audio file
song.ogg and then plays it online.
Stream object can only be read once and it is gone after reading. This means that only one of the five reading methods in the previous section can be used, otherwise, an error will be reported.
let text = await response.text();
let json = await response.json(); // Report an error
The above example uses
response.text() first and then reads the
Stream. After calling
response.json() later, there’s no content to read, so an error is reported. The
Response object provides the
response.clone() method, which creates a copy of the
Response object and implements multiple reads.
In the above example,
response.clone() made a copy of the
Response object and then read the same image twice. The
Response object also has a
Response.redirect() method, which is used to redirect the
Response result to the specified URL. This method is generally only used in
Service Worker, so I won’t introduce it here.
Response.body property is the underlying interface exposed by the
Response object. It returns a
ReadableStream object for user operations. It can be used to read content in blocks. One application is to display the progress of the download.
In the above example, the
response.body.getReader() method returns an iterator. The
read() method of this traverser returns an object each time, representing the content block read this time. The
done attribute of this object is a boolean value, used to judge whether it has been read. The
value attribute is an
arrayBuffer array, which represents the content of the content block. The
value.length attribute is the size of the current block.
The first parameter of
fetch() is the URL, and the second parameter can also be accepted as a configuration object to customize the HTTP request sent out.
optionObj of the above command is the second parameter. The HTTP request method, header, and data body are all set in this object. Here are some examples.
In the above example, the configuration object uses three attributes:
method：The HTTP request method, POST, DELETE, PUT are all set in this property.
headers：An object used to customize the header of the HTTP request.
body：The data body of the POST request.
Note that some headers cannot be set by the
headers attribute, such as
Host, etc. They are automatically generated by the browser and cannot be modified.
Submit JSON data
In the above example, the header
Content-Type should be set to
'application/json;charset=utf-8'. Because the default is to send plain text, the default value of
If there is a file selector in the form, you can use the writing of the previous example. The uploaded file is included in the entire form and submitted together. Another method is to add files with scripts, construct a form, and upload, please see the example below.
When uploading a binary file, there’s no need to modify the
Content-Type of the header — the browser will automatically set it.
Upload binary data directly
fetch() can also upload binary data directly, putting
arrayBuffer data in the
The completion of the second parameter of
fetch() API is as follows:
The bottom layer of the
fetch() request uses the interface of the
Request() object. The parameters are exactly the same, so the above API is also the API of
Request(). Among these attributes,
method have been given examples before. The following is an introduction to other attributes.
cache attribute specifies how to handle the cache. The possible values are as follows:
default：The default value is to find matching requests in the cache first.
no-store：Request the remote server directly and do not update the cache.
reload：Directly request the remote server and update the cache.
no-cache：Compare the server resources with the local cache and use the server resources when there is a new version. Otherwise use the local cache.
force-cache：Cache is the priority, and the remote server is only requested if there is no cache.
only-if-cached：Only check the cache. If the cache does not exist, a 504 error will be returned.
mode attribute specifies the requested mode. The possible values are as follows:
cors：The default value allows cross-domain requests.
same-origin：Only same-origin requests are allowed.
no-cors：The request method is limited to GET, POST and HEAD, and only a limited number of simple headers can be used, and cross-domain complex headers cannot be added, which is equivalent to the request that can be made by submitting the form.
credentials attribute specifies whether to send cookies. The possible values are as follows:
same-origin：By default, cookies are sent when requesting from the same origin, but not when requesting across domains.
include：Regardless of same-origin requests or cross-domain requests, cookies are always sent.
For cross-domain requests to send cookies, the
credentials attribute needs to be set to
signal attribute specifies an
AbortSignal instance to cancel the
fetch() request, see the next section for details.
keepalive attribute is used when the page is uninstalled to tell the browser to keep the connection in the background and continue to send data. A typical scenario is that when the user leaves the web page, the script submits some statistical information about the user’s behavior to the server. At this time, if the
keepalive attribute is not used, the data may not be sent because the browser has uninstalled the page.
redirect attribute specifies the processing method for HTTP redirects. The possible values are as follows:
fetch()follows HTTP redirects.
error：If a jump occurs,
fetch()will report an error.
fetch()does not follow the HTTP redirection, but the
response.urlproperty will point to the new URL, and the
response.redirectedproperty will become
true. The developer decides how to handle the redirection later.
integrity attribute specifies a hash value to check whether the data returned by the HTTP response is equal to the preset hash value. For example, when downloading a file, check whether the SHA-256 hash value of the file matches to ensure that it has not been tampered with.
referrer attribute is used to set the
referrer header of the
fetch() request. This attribute can be any string or an empty string (that is, no
referrer header is sent).
referrerPolicy attribute is used to set the rules of the
Referrer header. The possible values are as follows:
no-referrer-when-downgrade：The default value, the
Referrerheader is always sent, unless it is not sent when requesting HTTP resources from an HTTPS page.
Referrerheader is not sent.
Referrerheader only contains the domain name, not the complete path.
Referrerheader of the same-origin request contains the complete path, and the cross-domain request only contains the domain name.
same-origin：Cross-domain requests do not send
Referrer, but same-source requests are sent.
Referrerheader only contains the domain name. The
Referrerheader is not sent when the HTTPS page requests HTTP resources.
Referrerheader contains the full path for the same-origin request, and only the domain name for the cross-domain request. This header is not sent when the HTTPS page requests HTTP resources.
unsafe-url: No matter what the situation, always send the
fetch() request is sent, if you want to cancel halfway, you need to use the
In the above example, first create an
AbortController instance, then send a
fetch() request. The
signal property of the configuration object must specify that it receives the signal
Controller.signal sent by the
AbortController instance. The
Controller.abort method is used to signal the cancellation. At this time, the
abort event will be triggered. This event can be monitored, or you can determine whether the cancel signal has been sent through the
Controller.signal.aborted property. The following is an example of automatically canceling the request after one second: