Skip to main content
May 24, 2016
Mobile

Fetch (or the undeniable limitations of XHR)

Starting in EdgeHTML 14, coming with this summer’s Windows 10 Anniversary Update, Microsoft Edge will support the highly anticipated Fetch APIs. This represents the first step in our planned journey to supporting Service Worker in a future release. You can preview the Fetch APIs in Windows Insider Preview builds starting with EdgeHTML 14.14342.

XHR: the beginning

The journey to Fetch started with XMLHttpRequest (XHR), first pioneered in IE5 (as part of the MSXML ActiveX control) and now one of the foundational components of modern web functionality. Most sites today use XHR, often via a helper method that abstracts away its unpleasantness (such as the $.ajax() method in jQuery), rather than directly (for example, via the open method, setRequestHeader method, send method, and onreadystatechange event listener). One of the many reasons to use a JavaScript library such as jQuery is to make it easier to interface with XHR, especially when these libraries take advantage of new JavaScript concepts such as Promises.

Along came Fetch

Unfortunately, libraries aren’t well-equipped to interact with the low-level constructs of requests and responses, because the XHR APIs do not expose those concepts. More importantly, there hasn’t previously been a way to get at any resource request that occurs in the browser, for instance, the CSS files and image files included as part of the HTML of a page. Developers were only able to operate on requests and responses when making explicit XHR calls in JavaScript. This posed an interesting challenge for newer features, such as Service Worker, which needed to intercept any fetch that occurs in the browser, as well as features that relied on a standardized process by which resources can be retrieved, such as sendBeacon.

Because of this, the web needed a new spec to unify the concepts of fetching resources, CORS, and redirect handling across the web platform. There was also a need to introduce new APIs that would give access to the primitives that would allow interfacing with the requests and responses.

Why not extend XHR?

The XHR2 spec prioritizes backwards compatibility with the first iteration of XHR; this resulted in XHR2 being restricted to XHR’s dated object model. Building a brand new API from the ground up means it can be unshackled from the old API’s model. XHR could have been extended to have the same capabilities of the Fetch API, but it would have been an ad-hoc addition that would have made little sense in the context of any and all resource requests from the browser. Starting over and introducing the lower-level primitives that represent the requests and responses allowed for a far more extensible and robust API that also leverages new platform primitives such as Promises.

To fetch or not to fetch?

All of this raises the question of why developers should use the new Fetch APIs if they can already accomplish the same tasks with XHR. Let’s walk through a few of the several reasons for using the Fetch APIs: modern APIs, streaming, and future-proofing.

1. Modern APIs

The most obvious difference between XHR and the Fetch APIs is the actual amount of code needed for similar tasks.

Take the following common example of making a request for a JSON resource with XHR:

var xhr = new XMLHttpRequest();
xhr.open('GET', url);
xhr.responseType = 'json';
xhr.onload = function() {
console.log(xhr.response);
};
xhr.onerror = function() {
console.log("Houston, we've got a problem.");
};
xhr.send();

Compare the same request using Fetch:

fetch(url).then(function(response) {
return response.json();
}).then(function(jsonData) {
console.log(jsonData);
}).catch(function() {
console.log("Houston, we've got a problem.");
});

Slimming! Besides the fact that it takes less code to do the same thing, we are also able to take advantage of Promise-chaining rather than having countless callbacks that would result in a confusing mess. The Fetch APIs have been created with modern coding patterns in mind, along with sleek new interfaces, but we’re not done yet!

2. Streaming!

In addition to having a modernized API surface, developers get the fundamental primitives involved in a fetch operation: the Request and the Response objects. These low-level abstractions provide the flexibility to do more than what was previously possible with XHR. Specifically, we’re able to consume the underlying stream of a Response’s body instead of just retrieving the body as text.

Take the following example of streaming content from a large UTF-8 .txt file to a div on the page:

document.addEventListener('DOMContentLoaded', function (e) {
var url = 'LargeFile.txt';
var progress = 0;
var contentLength = 0;
fetch(url).then(function(response) {
// get the size of the request via the headers of the response
contentLength = response.headers.get('Content-Length');
var pump = function(reader) {
return reader.read().then(function(result) {
// if we're done reading the stream, return
if (result.done) {
return;
}
// retrieve the multi-byte chunk of data
var chunk = result.value;
var text = '';
// since the chunk can be multiple bytes, iterate through
// each byte while skipping the byte order mark
// (assuming UTF-8 with single-byte chars)
for (var i = 3; i < chunk.byteLength; i++) {
text += String.fromCharCode(chunk[i]);
}
// append the contents to the page
document.getElementById('content').innerHTML += text;
// report our current progress
progress += chunk.byteLength;
console.log(((progress / contentLength) * 100) + '%');
// go to next chunk via recursion
return pump(reader);
});
}
// start reading the response stream
return pump(response.body.getReader());
})
.catch(function(error) {
console.log(error);
});
});

We are now able to buffer data as it comes in, and we don’t have to wait until it’s all there. Streaming the response body improves the site’s memory usage and gives a greater perception of speed when trying to show content over slow connections. In XHR, the whole response would be buffered, rather than being able to operate on the data in chunks. It’s possible to set up a stream in XHR, but it would cause the responseText to continuously grow, and you would have to constantly retrieve the data manually from there. In addition, the Fetch APIs provide access to the actual bytes of the data when streaming, whereas XHR’s responseText is text-only meaning that it can be very limiting in some scenarios.

3. Future-proofing

As it stands, XHR is the de-facto method in JavaScript for retrieving resources from the network. Fetch is widely considered to be the way of the future. The groundwork has been laid out now so that some point in the future we’ll be able look back fondly at a time when XHR was a thing.

For the time being, many users are still running browsers that may not support fetch, so it makes sense for web developers to continue to use XHR. However, that doesn’t mean that they can’t take advantage of Fetch today; GitHub’s Fetch polyfill, which is built on top of XHR, provides a great starting point for supporting browsers that do not have Fetch (with some limitations such as a lack of true streaming). Meanwhile, developers can take advantage of new capabilities introduced by Fetch (like true streaming) to progressively enhance experiences for user-agents that do support Fetch.

The road to Service Worker

When we talk about Fetch we are not just talking about the APIs that are meant to succeed XHR, we are also talking about the underlying algorithms and process of retrieving resources. In fact, XHR has been rewritten to be defined in terms of the Fetch algorithms, effectively making Fetch a superset of XHR. The Fetch spec also defines how every resource request is routed to and intercepted by a Service Worker.

Our journey for web apps on Windows began back in 2011, and in January we outlined our roadmap for the Fetch, Service Worker, and Push standards as the next evolution for better performance, offline experiences, and increased user engagement. Our implementation begins with the APIs defined in the Fetch spec, available in Windows Insider Preview build 14342. Our next step is to complete our implementation of the spec, including the ability to perform a fetch in a Service Worker context. And so today, we’re excited to begin development of Service Worker in Microsoft Edge.

Go on, give it a try!

You don’t have to wait until later this summer to get your hands dirty with the Fetch APIs in Edge – you can preview this functionality today via the Windows Insider Program (Fetch requires build 14342 or higher). You’ll be fetching better than your pet in no time at all! We’re truly excited to deliver this set of APIs, and we look forward to your feedback and experimentation.

– Ali Alabbas, Program Manager, Microsoft Edge