Categories
JavaScript APIs

Measuring Navigation Time with the JavaScript Navigation API

To measure navigation events like page load and going to different pages, we can use the Navigation Timing API.

In this article, we’ll take a look at how to use it in various scenarios.

Navigation Timing API

We can use the Navigation Timing API to collect performance data on the client which can then be transmitted to a server. The API also lets us measure data that was previously difficult to obtain, like the amount of time needed to unload the previous page, the time it takes to look up domains, total time pent running window’s load handler, etc.

All timestamps returned are measured in milliseconds. All properties are read-only.

The main interface for measure navigation performance is the PerformanceNavigationTiming interface.

A simple example to measure page load time would be the following:

const perfEntries = performance.getEntriesByType("navigation");
const [p] = perfEntries;
const pageLoadTime = `p.loadEventEnd - p.loadEventStart;`
console.log(pageLoadTime)

The code above measures the time between the loadEventStart event and loadEventEnd event is triggered.

We can also use it to measure how long it takes the DOM of a document to load as follows:

const perfEntries = performance.getEntriesByType("navigation");
const [p] = perfEntries;
const domLoadTime = p.domContentLoadedEventEnd - p.domContentLoadedEventStart;
console.log(domLoadTime)

In the code above, we used the domContentLoadedEventEnd and domContentLoadedEventStart properties to get the timing of the when the DOM finished loading and when the DOM started loading with those 2 properties respectively.

We can also use the domComplete to get when the DOM finished loading and when the page is interaction with the interactive properties of a performance entry.

We can write the following:

const perfEntries = performance.getEntriesByType("navigation");
const [p] = perfEntries;
console.log(p.domComplete);

Likewise, we can do something similar to measuring unloading time:

const perfEntries = performance.getEntriesByType("navigation");
const [p] = perfEntries;
const domLoadTime = p.unloadEventEnd - p.unloadEventStart;
console.log(domLoadTime)

Other Properties

We can get the number of redirects since the last non-redirect navigation under the current browsing context with the redirectCount read-only property.

To use it, we can write the following:

const perfEntries = performance.getEntriesByType("navigation");
const [p] = perfEntries;
console.log(p.redirectCount);

The type property will get us the navigation type. The possible values of this property are 'navigate', 'reload', 'back_forward', or 'prerender'.

We can get the type as follows:

const perfEntries = performance.getEntriesByType("navigation");
const [p] = perfEntries;
console.log(p.type);

Methods

We can serialize a PerformanceNavigationTiming object with the toJSON method.

To use it, we can write:

const perfEntries = performance.getEntriesByType("navigation");
const [p] = perfEntries;
console.log(p.toJSON());

Then we get something like the following from the console.log:

{
  "name": "https://fiddle.jshell.net/_display/",
  "entryType": "navigation",
  "startTime": 0,
  "duration": 0,
  "initiatorType": "navigation",
  "nextHopProtocol": "h2",
  "workerStart": 0,
  "redirectStart": 0,
  "redirectEnd": 0,
  "fetchStart": 0.31999999191612005,
  "domainLookupStart": 0.31999999191612005,
  "domainLookupEnd": 0.31999999191612005,
  "connectStart": 0.31999999191612005,
  "connectEnd": 0.31999999191612005,
  "secureConnectionStart": 0.31999999191612005,
  "requestStart": 2.195000008214265,
  "responseStart": 89.26000000792556,
  "responseEnd": 90.5849999981001,
  "transferSize": 1435,
  "encodedBodySize": 693,
  "decodedBodySize": 1356,
  "serverTiming": [],
  "unloadEventStart": 95.75999999651685,
  "unloadEventEnd": 95.75999999651685,
  "domInteractive": 116.96000001393259,
  "domContentLoadedEventStart": 116.97000000276603,
  "domContentLoadedEventEnd": 117.20500001683831,
  "domComplete": 118.64500000956468,
  "loadEventStart": 118.68000001413748,
  "loadEventEnd": 0,
  "type": "navigate",
  "redirectCount": 0
}

Conclusion

The PerformanceNavigationTiming interface is very handy for measure page navigation and loading times.

It’s information that’s hard to get any other way.

The interface provides us with various read-only properties that give us the timing of when various page load and navigation-related events occurred. All times are measured in milliseconds.

Categories
JavaScript APIs

Measuring JavaScript App Performance with the Performance API

With the JavaScript Performance API, we have an easy way to measure the performance of a front-end JavaScript apps.

In this article, we’ll look at how to use it to measure our app’s performance.

Performance

We can measure the performance of an app with a few methods. The Performance API provides a precise and consistent definition of time. The API will gives us with a high precision timestamp to mark the time when a piece of code starts running and finishes.

The timestamp is in milliseconds and it should be accurate to 5 microseconds. The browser can represent the value as time in milliseconds accurate to a millisecond if there’re hardware or software constraints that make our browser unable to provide value with the higher accuracy.

We can use it as in the following example:

const startTime = performance.now();
for (let i = 0; i <= 10000; i++) {
  console.log(i);
}
const endTime = performance.now();
console.log(endTime - startTime)

In the code above, we used the performance object to mark the time when the loop starts running and finishes running.

Then we logged the time by subtracting endTime by startTime to give us the elapsed time when the loop ran in milliseconds.

Serializing the Performance object

The performance object is serialized by the toJSON method.

We can use it as follows:

const js = window.performance.toJSON();
console.log(JSON.stringify(js));

Then we get something like:

{"timeOrigin":1579540433373.9158,"timing":{"navigationStart":1579540433373,"unloadEventStart":1579540433688,"unloadEventEnd":1579540433688,"redirectStart":0,"redirectEnd":0,"fetchStart":1579540433374,"domainLookupStart":1579540433376,"domainLookupEnd":1579540433423,"connectStart":1579540433423,"connectEnd":1579540433586,"secureConnectionStart":1579540433504,"requestStart":1579540433586,"responseStart":1579540433678,"responseEnd":1579540433679,"domLoading":1579540433691,"domInteractive":1579540433715,"domContentLoadedEventStart":1579540433715,"domContentLoadedEventEnd":1579540433716,"domComplete":1579540433716,"loadEventStart":1579540433716,"loadEventEnd":0},"navigation":{"type":0,"redirectCount":0}}

logged.

Measuring Multiple Actions

We can use the mark method to mark our actions and the use the measure method to measure the time between actions by passing in the names.

For example, we can measure time with markings as follows:

performance.mark('beginLoop');
for (let i = 0; i < 10000; i++) {
  console.log(i);
}
performance.mark('endLoop');
performance.measure('measureLoop', 'beginLoop', 'endLoop');
console.log(performance.getEntriesByName('measureLoop'));

In the code above, we called the mark method before the loop starts and after the loop ends.

Then we call the measure method with a name we create to get the time difference later and both markers so that we can get the time from them and get the time difference.

Then we called performance.getEntriesByName(‘measureLoop’) to get the computed duration with the duration property of the returned object.

‘measureLoop’ is the name we made up to get the time difference by name, and ‘beginLoop' and 'endLoop' are our time markers.

We can get entries marked by the mark method with the getEntriesByType method. It takes a string for the type. To do this, we can write:

performance.mark('beginLoop');
for (let i = 0; i < 10000; i++) {
  console.log(i);
}
performance.mark('endLoop');
performance.measure('measureLoop', 'beginLoop', 'endLoop');
console.log(performance.getEntriesByType("mark"))

Then the console.log should get us the following:

[
  {
    "name": "beginLoop",
    "entryType": "mark",
    "startTime": 133.55500000761822,
    "duration": 0
  },
  {
    "name": "endLoop",
    "entryType": "mark",
    "startTime": 1106.3149999827147,
    "duration": 0
  }
]

There’s also a getEntriesByName method which takes the name and the type as the first and second arguments respectively.

For example, we can write:

performance.mark('beginLoop');
for (let i = 0; i < 10000; i++) {
  console.log(i);
}
performance.mark('endLoop');
performance.measure('measureLoop', 'beginLoop', 'endLoop');
console.log(performance.getEntriesByName('beginLoop', "mark"));

Then we get:

[
  {
    "name": "beginLoop",
    "entryType": "mark",
    "startTime": 137.6299999828916,
    "duration": 0
  }
]

from the console.log .

We can also use getEntries by passing in an object with the name and entryType properties as follows:

performance.mark('beginLoop');
for (let i = 0; i < 10000; i++) {
  console.log(i);
}
performance.mark('endLoop');
performance.measure('measureLoop', 'beginLoop', 'endLoop');
console.log(performance.getEntries({
  name: "beginLoop",
  entryType: "mark"
}));

Then we get something like:

[
  {
    "name": "[https://fiddle.jshell.net/_display/](https://fiddle.jshell.net/_display/)",
    "entryType": "navigation",
    "startTime": 0,
    "duration": 0,
    "initiatorType": "navigation",
    "nextHopProtocol": "h2",
    "workerStart": 0,
    "redirectStart": 0,
    "redirectEnd": 0,
    "fetchStart": 0.2849999873433262,
    "domainLookupStart": 0.2849999873433262,
    "domainLookupEnd": 0.2849999873433262,
    "connectStart": 0.2849999873433262,
    "connectEnd": 0.2849999873433262,
    "secureConnectionStart": 0.2849999873433262,
    "requestStart": 2.3250000085681677,
    "responseStart": 86.29499998642132,
    "responseEnd": 94.03999999631196,
    "transferSize": 1486,
    "encodedBodySize": 752,
    "decodedBodySize": 1480,
    "serverTiming": [],
    "unloadEventStart": 101.23999998904765,
    "unloadEventEnd": 101.23999998904765,
    "domInteractive": 126.96500000311062,
    "domContentLoadedEventStart": 126.9800000009127,
    "domContentLoadedEventEnd": 127.21500001498498,
    "domComplete": 128.21500000427477,
    "loadEventStart": 128.2249999931082,
    "loadEventEnd": 0,
    "type": "navigate",
    "redirectCount": 0
  },
  {
    "name": "[https://fiddle.jshell.net/js/lib/dummy.js](https://fiddle.jshell.net/js/lib/dummy.js)",
    "entryType": "resource",
    "startTime": 115.49500000546686,
    "duration": 0,
    "initiatorType": "script",
    "nextHopProtocol": "h2",
    "workerStart": 0,
    "redirectStart": 0,
    "redirectEnd": 0,
    "fetchStart": 115.49500000546686,
    "domainLookupStart": 115.49500000546686,
    "domainLookupEnd": 115.49500000546686,
    "connectStart": 115.49500000546686,
    "connectEnd": 115.49500000546686,
    "secureConnectionStart": 0,
    "requestStart": 115.49500000546686,
    "responseStart": 115.49500000546686,
    "responseEnd": 115.49500000546686,
    "transferSize": 0,
    "encodedBodySize": 0,
    "decodedBodySize": 0,
    "serverTiming": []
  },
  {
    "name": "[https://fiddle.jshell.net/css/result-light.css](https://fiddle.jshell.net/css/result-light.css)",
    "entryType": "resource",
    "startTime": 115.77999999281019,
    "duration": 0,
    "initiatorType": "link",
    "nextHopProtocol": "h2",
    "workerStart": 0,
    "redirectStart": 0,
    "redirectEnd": 0,
    "fetchStart": 115.77999999281019,
    "domainLookupStart": 115.77999999281019,
    "domainLookupEnd": 115.77999999281019,
    "connectStart": 115.77999999281019,
    "connectEnd": 115.77999999281019,
    "secureConnectionStart": 0,
    "requestStart": 115.77999999281019,
    "responseStart": 115.77999999281019,
    "responseEnd": 115.77999999281019,
    "transferSize": 0,
    "encodedBodySize": 49,
    "decodedBodySize": 29,
    "serverTiming": []
  },
  {
    "name": "beginLoop",
    "entryType": "mark",
    "startTime": 128.3699999912642,
    "duration": 0
  },
  {
    "name": "measureLoop",
    "entryType": "measure",
    "startTime": 128.3699999912642,
    "duration": 887.0650000171736
  },
  {
    "name": "endLoop",
    "entryType": "mark",
    "startTime": 1015.4350000084378,
    "duration": 0
  }
]

from the console.log .

With markers, we can name our time markers, so we can measure multiple actions.

Clearing Actions

We can clear performance markers by calling the clearMarks method. For example, we can do that as follows:

performance.mark("dog");
performance.mark("dog");
performance.clearMarks('dog');

There’s also a clearMeasures method to clear measurements and clearResourceTimings to clear performance entries.

For example, we can use it as follows:

performance.mark('beginLoop');
for (let i = 0; i < 10000; i++) {
  console.log(i);
}
performance.mark('endLoop');
performance.measure('measureLoop', 'beginLoop', 'endLoop');
performance.clearMeasures("measureLoop");
console.log(performance.getEntriesByName('measureLoop'));

Then we should see an empty array when we call getEntriesByName .

To remove all performance entries, we can use the clearResourceTimings method. It clears the performance data buffer and sets the performance data buffer to zero.

It takes no arguments and we can use it as follows:

performance.mark('beginLoop');
for (let i = 0; i < 10000; i++) {
  console.log(i);
}
performance.mark('endLoop');
performance.measure('measureLoop', 'beginLoop', 'endLoop');
performance.clearResourceTimings();

In the code above, we called the clearResourceTimings method to reset the buffers and performance data to zero so that we can run our performance tests with a clean slate.

Conclusion

We can use the Performance API to measure the performance of a piece of front-end JavaScript code.

To do this, we can use the now method to get the timestamp and then find the difference between the 2.

We can also use the mark method to mark the time and then use the measure method to compute the measurement.

There’re also various ways to get the performance entries and clear the data.

Categories
JavaScript APIs

Using the Browser History API

To navigate back and forth between pages, we can use the History API that comes with most modern browsers.

In this article, we’ll look at how to use it to navigate between pages of the same origin.

Methods in the History API

The History API has a few methods to move back and forth between pages.

There’s the back method to go back to the previous page.

For example, we can use it as follows:

window.history.back()

This is the same as clicking the back button on the browser.

We can go forward by using the forward method as follows:

window.history.forward()

Moving to a Specific Point in History

We can use the go method to load a specific page from session history, identified by its relative position on the current page.

The current page is 0, so a negative integer is the pages before and a positive number is the pages after.

For example:

window.history.go(-1)

is the same as calling back().

and:

window.history.go(1)

is the same as calling forward() .

Calling go with no argument or 0 is the same as refreshing the page.

Photo by Giammarco Boscaro on Unsplash

pushState

The pushState method let us go to the page with the specified URL. It takes 3 arguments. They are:

  • state — this is a Javascript object which is associated with the new history entry created by pushState() . The state object can be anything that can be sanitized. It’s saved onto the user’s disk so that it can be restored when the user restarts the browser. If the state object has a serialized representation that’s bigger than itself, then this method will throw an exception.
  • title — most browsers ignore this string parameter.
  • url — the new history entry’s URL. The browser won’t attempt to load the URL after calling pushState . However, it might load when we restart the browser. The URL can be absolute or relative. The URL must be the same origin as the current URL. Otherwise, an exception will be thrown.

We don’t have to change the URL if we don’t want to. Whatever it is, it must be in the same origin as the current URL.

For example, we can use it as follows:

window.onpopstate = function(event) {
  console.log(
    `location ${document.location} state ${JSON.stringify(event.state)}`
  );
};

window.history.pushState(
  {
    foo: "bar"
  },
  "",
  "/foo"
);

window.history.pushState(
  {
    bar: "baz"
  },
  "",
  "/bar"
);

window.history.back();
window.history.back();
window.history.go(2);
window.history.go(-2);

Then we get:

location https://ib3i4.csb.app/foo state {"foo":"bar"}
location https://ib3i4.csb.app/bar state null
location https://ib3i4.csb.app/bar state {"bar":"baz"}
location https://ib3i4.csb.app/bar state null

from the console.log .

onpopstate is called whenever we navigate between pages.

When we go back, we get the state logged only the first time.

Likewise, when we go forward, we get the state only logged once.

replaceState

The replaceState method is similar to pushState, but it modifies the current history entry instead of adding a new one.

It takes the same arguments as the pushState.

For example, we can use it as follows:

window.onpopstate = function(event) {
  console.log(
    `location ${document.location} state ${JSON.stringify(event.state)}`
  );
};

window.history.pushState(
  {
    bar: "bar"
  },
  "",
  "/foo"
);

window.history.go(0);

window.history.replaceState(
  {
    bar: "baz"
  },
  "",
  "/bar"
);

window.history.go(0);

Then we get:

location https://ib3i4.csb.app/foo state {"bar":"bar"}
location https://ib3i4.csb.app/bar state {"bar":"baz"}

As we can see, the entry of /bar ‘s state went from {“bar”:”bar”} to {“bar”:”baz”} once we refreshed it the second time, so the state was replaced.

Conclusion

We can use the History API by calling the methods in the window.history object.

We can go forward in history with forward, backward with back. To go to any page in history, we can call go with a number.

Also, we can call pushState to push a new entry to the history without going to it immediately. It can load the page on restart or when we call the methods above.

We can use replaceState to replace the current history entry. It takes the same arguments as pushState .

Every page must be of the same origin when using this API.

Categories
JavaScript APIs

Introducing the Page Visibility API

Since most modern browsers are tabbed, it’s possible that a webpage may reside in a tab that stays in the background and not visible to the user.

The Page Visibility API can provide us with insight as to whether a page is visible to the user.

In this article, we’ll look at the Page Visibility API, its use cases, and how to use it.

Visibility Change Event

When a user minimizes the window or switches to another tab, the Page Visibility API sends a visibilitychange event to let listeners know that the state of the page has changed.

We can handle the event when it’s triggered and do something according to the visibility state. For example, we can pause the video when the page is hidden from view.

The visibility state of an iframe is the same as the parent document that the iframe is in. Hiding an iframe with CSS doesn’t trigger visibility events or change the state of the document contained within the iframe .

Use Cases

There’re many use cases for using the API. Some of them include the following:

  • pausing an image carousel when the page is hidden
  • stop polling the server for information when the page is hidden
  • prerendering a page to keep an accurate count of page views
  • switch off the sound when the page is not being viewed

Without the Page Visibility API, developers resort to imperfect solutions like listening to the blur or focus events of the window to help detect whether the page is visible or not.

They don’t tell if they’re hidden or not, just whether they’re in focus or not.

Policies that Help Background Page Performance

Most browsers do a few things to help save resources when a page isn’t in view.

requestAnimationFrame callback won’t be called to improve performance and battery life when a page is in the background.

setTimeout and other timers are throttles in the background or inactive tabs to improve performance.

Throttling is also done in browsers to limit CPU usage by background tabs.

Each background tab has its own time budget between -150ms and 50ms.

Browser windows are subjected to throttling after 30 seconds in Firefox and 10 seconds in Chrome.

Timer tasks are only permitted when the time budget is non-negative.

Once the timer’s code finishes running, the duration it takes to execute is subtracted from the time budget.

The budget regenerates at a rate of 10ms per second in both Firefox and Chrome.

Some processes ate exempt from throttling behavior. Tabs that are playing audio are considered foreground tabs and aren’t subject to throttling.

Code that uses real-time network connections goes unthrottled to prevent the closing of these connections.

IndexedDB processes are also left unthrottled to avoid timeouts.

The Page Visibility API can let us stop these things manually if we want to do so.

Photo by Oscar Sutton on Unsplash

Using the Page Visibility API

The Page Visibility API is part of the document object.

We can use it by checking the document.hidden property or the document.visibilityState property. They’re both read-only.

To watch for changes in both, we can listen to the visibilitychange event.

To do this we can use the following example. Our example will pause a video when we switch to a different tab. First, we add the HTML code for the video as follows:

<video controls src='https://sample-videos.com/video123/mp4/240/big_buck_bunny_240p_30mb.mp4'></video>

Then in our JavaScript code, we can listen to the visibilitychange event as follows:

const video = document.querySelector('video');

document.addEventListener('visibilitychange', () => {
  if (document.visibilityState !== 'visible') {
    video.pause();
  }
})

In our event listener callback, we pause the video when the visibilityState isn’t ‘visible’ , which means the user can’t see the tab either by navigating away from the tab or window, minimizing the window, or turned the screen off.

The alternative to this is to set the event handler to the onvisibilitychange property of document.

document.visibilityState can take on these 3 values:

  • visible — the page is visible to the user as a foreground tab
  • hidden — the page isn’t visible to the user, either because it’s in the background, or the minimized or the device’s screen is off.
  • prerender — the page is being prerendered and isn’t visible to the user. A document may start in this state, but will never switch to this state from any other state since it can only prerender once. Not all browsers support prerendering.
  • unloaded — the page is being unloaded from memory. Not all browsers support this.

Compatibility

This API has been supported for a while. Chrome since version 33 supports this API. Edge, Firefox, IE 10 or later, and Safari 7 or later all support this API. Mobile versions of these browsers also support this API.

The Page Visibility API is useful for detecting the visibility state of the page. We can listen to the visibilitychange event and then get the visibility state with document.visibilityState and so what we want with it.

Categories
JavaScript APIs

Make HTTP Requests with the Fetch API

Before the Fetch API, HTTP requests were made with the XmlHttpRequest object. It’s more difficult to use and it’s not promised based since it was added before promises were built into JavaScript.

Now, we can use the Fetch API to make HTTP requests much more easily.

With it, we have a generic definition of Request and Response objects along with other things for network requests. This allows them to be used whenever they’re needed in the future.

It also provides a definition for related concepts such as CORS and HTTP origin header semantics, replacing existing definitions of them elsewhere.

In this article, we’ll look at how to make client-side HTTP requests with the Fetch API.

Making HTTP Requests

Making HTTP requests starts by using the fetch method. It takes one mandatory argument, which is the path to the resource which we want to fetch.

It returns a promise that resolves to the Response to that request, whether it’s successful or not. We can optionally pass in an init options object as the argument.

Once the Response is retrieved, there’re a number of methods to define what body content is and how it should be handled.

The promise returned by fetch won’t reject HTTP error status even if the response is 404 or 500. It’ll resolve normally with ok status set to false .

fetch won’t receive cross-site cookies. There’s no way to establish a cross-site session using fetch .

fetch won’t send cookies unless we set the credentials init option.

We can call the fetch method as follows:

(async () => {
  const response = await fetch('[https://jsonplaceholder.typicode.com/todos/1'](https://jsonplaceholder.typicode.com/todos/1%27))
  const json = await response.json();
  console.log(json);
})();

The code above gets makes a GET request with fetch and then converts it to a JavaScript object with the json() method of the Response object. Then we can log it in the console.log .

This is the simplest case for an HTTP request. We can also add more options in the second argument. We can set the following options:

  • method — request method
  • headers — request headers that we want to add
  • body — request body. It can be Blob, BufferSource, FormData, URLSearchParams, USVString, or ReadableStream object. GET or HEAD requests can’t have a body.
  • mode — mode for the request. It can becors, no-cors, or same-origin
  • credentials — request credentials we want to use for the requests. Possible values are omit, same-origin, or include . This option must be provided to automatically send cookies for the current domain. Starting with Chrome 50, this property also takes a FederatedCredential instance or a PasswordCredential instance.
  • cache — cache mode we want to use for the request
  • redirect — redirect mode to use. Set this to follow for automatically follows redirects, error to abort with an error if a redirect occurs, or manual to handle redirects manually
  • referrer — a string specifying no-referrer, client, or a URL. Default value is client
  • referrerPolicy — specifies the value of the referrer HTTP header. Can be one of no-referrer, no-referrer-when-downgrade, origin, origin-when-cross-origin, unsafe-url
  • integrity — subresource integrity value of the request
  • keepalive — set this optional to allow the request to outlive the page
  • signal — an AbortSignal object instance to let us communicate with a fetch request and abort it via an AbortController .

For example, we can make a basic POST request by writing:

(async () => {
  const response = await fetch('[https://jsonplaceholder.typicode.com/posts'](https://jsonplaceholder.typicode.com/posts%27), {
    method: 'POST',
    body: JSON.stringify({
      title: 'title',
      body: 'body',
      userId: 1
    }),
    headers: {
      "Content-type": "application/json; charset=UTF-8"
    }
  })
  const json = await response.json();
  console.log(json);
})();

We set all the options in the object of the second argument, including the body and headers.

To upload a file, we can get the file from a file input. Given that we have it in the HTML:

<input type='file' id='file-input'>

Then we can write the following to watch for changes in the value of the file input and upload the file to the server:

const upload = async (file) => {
  const response = await   fetch('[https://localhost/'](https://jsonplaceholder.typicode.com/posts%27), {
    method: 'POST',
    body: file,
    headers: {
      'Content-Type': 'multipart/form-data'
    }
  })
  const json = await response.json();
  console.log(json);
};

const input = document.getElementById('file-input');
input.addEventListener('change', () => {
  upload(input.files[0]);
}, false);

Note that the header may change depending on the server we use. The general idea is that we get the file from the input and then send it in the body of the request.

Manipulating the Response

The Response has a few properties for getting data and manipulating it. We can use the error method to get the error, redirect method creates a new response with a different URL.

The Body object, which the Response object implements have the formData method for reading FormData responses. Likewise, there’s the json method for reading JSON responses and text for reading plain text. They all resolve to promises with the corresponding objects. arrayBuffer method will read binary data and resolves to an ArrayBuffer . blob method reads binary data and resolves it to a Blob .

Value properties that may be useful to us include headers to get the response headers, ok to check if the request was successful, redirect to check whether redirect happened. status is the response status code, statusText has the text that corresponds to the status code. url has the response URL, body has the response body.

The Fetch API is much better than XmlHttpRequest for making HTTP requests. It lets us make most kinds of requests, and it’s promise based so that they can be run sequentially with other promises.

It supports both text and binary bodies. Now we don’t need a third-party HTTP client to make client-side HTTP requests.

Request and Response objects are also standardized so that they can used with other APIs.