What are the differences between `XMLHttpRequest` and `fetch()` in JavaScript and browsers?
TL;DR
XMLHttpRequest (XHR) and fetch() API are both used for asynchronous HTTP requests in JavaScript (AJAX). fetch() offers a cleaner syntax, promise-based approach, and more modern feature set compared to XHR. However, there are some differences:
XMLHttpRequestuses event callbacks, whilefetch()utilizes promise chaining.- Both APIs support request headers and common body types; Fetch exposes
Headers,Request, andResponseabstractions and integrates with streams. - A Fetch promise rejects for network, CORS, and abort failures but fulfills for HTTP error statuses, so callers must check
response.okorresponse.status. - Both APIs use the browser's HTTP cache. Fetch additionally exposes a
cacherequest option. fetch()requires anAbortControllerfor cancelation, whileXMLHttpRequestprovides anabort()method.- XHR exposes convenient upload and download progress events. Fetch response bodies are streams, so download progress can be measured manually, but browsers still lack an equally convenient standard Fetch upload-progress API.
- XHR is a browser API. Fetch is a web standard also implemented by current Node.js and several other runtimes; check the target runtime rather than assuming universal support.
These days fetch() is preferred for its cleaner syntax and modern features.
XMLHttpRequest vs fetch()
Both XMLHttpRequest (XHR) and fetch() are ways to make asynchronous HTTP requests in JavaScript. However, they differ significantly in syntax, promise handling, and feature set.
Syntax and usage
XMLHttpRequest is event-driven and requires attaching event listeners to handle response/error states. The basic syntax for creating an XMLHttpRequest object and sending a request is as follows:
xhr is an instance of the XMLHttpRequest class. The open method is used to specify the request method, URL, and whether the request should be asynchronous. The onload event is used to handle the response, and the send method is used to send the request.
fetch() returns a promise that fulfills with a Response or rejects on a request failure. A basic GET request looks like this:
Request headers
Both APIs can set permitted request headers. Fetch also provides a dedicated Headers abstraction; browsers restrict certain headers for security regardless of which API is used.
XMLHttpRequest supports setting request headers using the setRequestHeader method:
For fetch(), headers are passed as an object in the second argument to fetch():
Request body
Both XMLHttpRequest and fetch() support sending request bodies. However, fetch() provides more flexibility in terms of sending request bodies, as it supports sending JSON data, form data, and more.
XMLHttpRequest supports sending request bodies using the send method:
fetch() supports sending request bodies using the body property in the second argument to fetch():
Response handling
XMLHttpRequest provides a responseType property to set the response format that we are expecting. responseType is 'text' by default but it supports types like 'text', 'arraybuffer', 'blob', 'document' and 'json'.
On the other hand, fetch() provides a unified Response object with methods like .json() and .text() for accessing data.
Error handling
The APIs expose errors differently. XHR uses events and status fields. Fetch uses promises, but a fulfilled Fetch promise can still contain an HTTP error response.
XMLHttpRequest supports error handling using the onerror event:
fetch() supports error handling using the catch() method on the returned Promise:
Caching control
Both APIs participate in normal HTTP caching. With Fetch, the cache request option gives code an explicit way to choose how the request interacts with the browser cache:
Other values for the cache option include default, no-store, reload, no-cache, force-cache, and only-if-cached.
Cancelation
In-flight XMLHttpRequests can be canceled by running the XMLHttpRequest's abort() method. An abort handler can be attached by assigning to the .onabort property if necessary:
Aborting a fetch() requires creating an AbortController object and passing its signal as the signal property of the options object when calling fetch().
Progress support
XMLHttpRequest supports tracking the progress of requests by attaching a handler to the XMLHttpRequest object's progress event. This is especially useful when uploading large files such as videos to track the progress of the upload.
The callback assigned to onprogress is passed a ProgressEvent:
- The
loadedfield on theProgressEventis a 64-bit integer indicating the amount of work already performed (bytes uploaded/downloaded) by the underlying process. - The
totalfield on theProgressEventis a 64-bit integer representing the total amount of work that the underlying process is in the progress of performing. When downloading resources, this is theContent-Lengthvalue of the HTTP response.
Fetch response bodies are readable streams, so download progress can be measured by reading chunks and comparing their total size with Content-Length when that header is present. Browser Fetch does not yet provide XHR's straightforward upload.onprogress equivalent for upload progress.
Choosing between XMLHttpRequest and fetch()
For new promise-based code, Fetch is usually the default. XHR remains useful when straightforward browser upload-progress events are a requirement. Neither API bypasses CORS, and Fetch's HTTP-status handling must be implemented explicitly.
Further reading
- Fetch API | MDN
- XMLHttpRequest | MDN
- Request: cache property | MDN
- XMLHttpRequest | JavaScript.info
- This API is so Fetching! - Mozilla Hacks