laitimes

Battle of Ajax: XMLHttpRequest vs. Fetch API

Author | Craig Buckler

Translated by | Phoenix

Planning | Yan Garden

This article was originally published on the OpenReplay blog and translated and shared by InfoQ Chinese.

Ajax is the core technology behind most web applications, allowing pages to make asynchronous requests to web services, so data can display data without going through a page round trip to the server without refresh.

The term Ajax is not a technique, but rather a method of loading server data from client script. Several options have been introduced over the years, and there are currently two main approaches, one or both of which most JavaScript frameworks use.

In this article, we'll examine the pros and cons of early XMLHttpRequest and modern Fetch to determine which Ajax API is best for your app.

XMLHttpRequest

XMLHttpRequest first appeared in 1999 as a non-standard Internet Explorer 5.0 ActiveX component, which Microsoft developed to support browser-based versions of Outlook, the most popular (or touted) data format at the time, in addition to XMLHttpRequest supporting text and json that had not yet been invented.

Jesse James Garrett proposed the concept of "AJAX" in his 2005 article AJAX: A New Approach to Web Applications, when AJAX-based applications such as Google Email and Google Maps already existed, but the term incentivized developers and caused an explosion of smooth Web 2.0 experiences.

AJAX is an abbreviation for "Asynchronous JavaScript and XML", although strictly speaking, developers do not need to use asynchronous methods, JavaScript, or XML. We will now use the generic term "Ajax" for any client-side process that gets data from the server and updates the DOM without refreshing the entire page.

All major browsers support XMLHttpRequest and became the official web standard in 2006. Here's a simple example of getting data from your domain/service/endpoint and then displaying the JSON result as text in the console:

The onreadystatechange callback function runs several times during the request's lifecycle; the readyState property of the XMLHttpRequest object returns the current state:

0 (uninitialized) - The request was not initialized

1 (loading) - The server connection is established

2 (loaded) - Request received

3 (interactive) - Process the request

4 (complete)- The request is complete and the response is ready

A few functions can do a lot of things before reaching state 4.

Fetch

Fetch is a modern promise-based Ajax request API that first appeared in 2015 and is supported in most browsers. It is not built on XMLHttpRequest and provides better consistency with a cleaner syntax. The following Promise chain function is the same as the XMLHttpRequest example above:

Or you can use async/await:

Fetch is clearer, cleaner, and often used in Service workers.

Open source session replay

OpenReplay is an open-source alternative to FullStory and LogRocket that provides complete observability by playing back everything the user does on your application and showing the action stack for each issue. OpenReplay is self-hosted and gives you full control over your data.

Happy debugging! Modern front-end teams – start monitoring your web applications freely.

Turn 1: Fetch wins

Compared to the stale XMLHttpRequest, the Fetch API has several advantages in addition to having a clearer and more concise syntax.

Headers, request, and response objects

In the simple fetch() example above, the URL endpoint is defined using a string, or a configurable Request object can be passed, which provides a series of properties about the call:

The Response object provides similar access to accessing all the details:

The Headers object provides a simple interface to set header information in a request or get header information in a response:

Cache control

Managing caching in XMLHttpRequest is challenging, and you may find it necessary to append a random query string value to bypass the browser cache, and the Fetch method has support for caching built into the second parameter, the init object:

The cache can be set to:

'default' – if there is a new (unexpired) match, the browser cache is used; if not, the browser issues a conditional request to check if the resource has changed and, if necessary, makes a new request

'no-store' – bypasses the browser cache and the network response does not update it

'reload' – bypasses the browser cache, but the network response updates it

'no-cache' – similar to 'default', except that a conditional request is always made

'force-cache' – if possible, use the cached version, even if it is obsolete

'only-if-cached' – the same force-cache, except that there are no network requests

Cross-domain control

Sharing resources across domains allows client script to make Ajax requests to another domain, provided that the server allows the source domain in the Access-Control-Allow-Origin response header; if this parameter is not set, both fetch() and XMLHttpRequest will fail. However, Fetch provides a schema property that allows you to set the 'no-cors' property in the init object of the second parameter.

This returns a response that cannot be read but can be used by other APIs. For example, you can use the Cache API store to store back and use it later, perhaps returning an image, script, or CSS file from a Service Worker.

Credential control

XMLHttpRequest always sends browser cookies, and the Fetch API does not send cookies unless you explicitly set the credentials attribute in the second parameter, the init object.

Credentials can be set to:

'omit' - exclude cookies and HTTP authentication entries (default)

'same-origin' – contains the credentials for requests to the same-origin url

'include' – contains all requested credentials

Note that include was the default in earlier API implementations, and if your users might be running older browsers, you'll have to explicitly set the credentials property.

Redirection control

By default, both fetch() and XMLHttpRequest follow server redirection. However, fetch() provides an alternative option in the second parameter, the init object:

redirect can be set to:

'follow' – follows all redirects (default)

'error' – abort (deny) when a redirect occurs

'manual' – Returns a manually processed response

data stream

XMLHttpRequest reads the entire response into a memory buffer, but fetch() can stream request and response data, a new technique that allows you to process smaller chunks of data when sending or receiving. For example, you can process information in a multi-megabyte file before a full download, and the following example converts an incoming (binary) block of data into text and outputs it to the console. On slower connections, you'll see smaller chunks of data arrive in longer periods of time.

Server-side support

Fully supported for Fetch in Deno and Node 18, using the same API on the server and client helps reduce cognitive costs, and also provides the possibility of running homogeneous JavaScript libraries anywhere.

Round 2: XMLHttpRequest wins

Despite its flaws, XMLHttpRequest has some tricks to outdo ajax Fetch().

Progress support

We can monitor the progress of the request by attaching a handler to the progress event of the XMLHttpRequest object. This is especially useful when uploading large files, such as photos:

The object passed by the event handler has three properties:

lengthComputable – set to true if progress can be calculated

total — The total amount of work or content length of the message body

loaded – the amount of work or content done so far

The Fetch API does not provide any way to monitor the progress of uploads.

Timeout support

The XMLHttpRequest object provides a timeout attribute that can be set to the number of milliseconds allowed to run before the request is automatically terminated; if it times out, a timeout event is triggered to handle:

A function can be encapsulated in fetch() to implement the timeout function:

Alternatively, you can use Promise.race():

Neither of these methods is easy to use, and the request will continue to run in the background.

Support for discontinuation

Running requests can be canceled via the abort() method of XMLHttpRequest, and if necessary, an abort event can be attached to handle:

You can abort a fetch(), but it's not so straightforward and requires an AbortController object:

When fetch() aborts, the catch() block executes.

More explicit fault detection

When a developer first uses fetch(), it seems logical to assume that an HTTP error, such as a 404 Not Found or 500 Internal Server error, will trigger a Promise rejection and run the associated catch() block, but it is not: Promise successfully resolves these responses, and the rejection only occurs if the network is not responding or the request is interrupted.

The response object of fetch() provides status and ok attributes, but it is not always explicitly necessary to check them, and XMLHttpRequest is more explicit because a single callback function handles every result: you should see the stuatus check in every example.

Browser support

I hope you don't have to support Internet Explorer or browser versions prior to 2015, but if that's the case, XMLHttpRequest is your only option. XMLHttpRequest is also stable, and the API is unlikely to be updated. Fetch is relatively new and missing a few key features, and while updates are unlikely to break the code, you can expect some maintenance.

Which API should I use?

Most developers will use the updated Fetch API, which has a cleaner syntax and more advantages than XMLHttpRequest; that is, many of these benefits have specific use cases, but they are not required in most applications. There are only two cases where XMLHttpRequest is still essential:

You're supporting very old browsers – that demand will decline over time.

You need to show the upload progress bar. Fetch will be supported later, but it may take several years.

Both of these options are fun and it's worth learning more about them!

https://blog.openreplay.com/ajax-battle-xmlhttprequest-vs-the-fetch-api

Read on