paulgorman.org/technical

JavaScript Ajax Fetch

(2018)

(See also https://paulgorman.org/technical/api-design.txt.html .)

The Fetch API simplifies AJAX calls compared to the old XMLHttpRequest, without resorting to jQuery.

The fetch() method takes one mandatory argument, the resource to fetch. It returns a promise that resolves to the response from that request (successful or not). The fetch() method accepts a second, optional argument to initialize the Request.

The Fetch API makes use of JavaScript promises (introduced in ECMAScript 6). A promise simplifies an asynchronous operation by acting as a placeholder.

Any given promise exists in one of three possible states:

Promises also facilitate error handling in a manner similar to try/catch but with then.

then takes one or two arguments: a callback for success, and a second for failure. Chain thens together to transform values (by returning a value) or run a series of additional asynchronous actions (by returning a promise). If a then explicitly returns a value, that value will be available for a subsequent chained then.

fetch('/api/v1/hello')
.then(

	function(response) {
		response.text()
		.then(
			function(text) {
				console.log(text);
			},
			function(error) {
				console.log('Failed to extract text from hello response', error);
			}
		)
	},

	function(error) {
		console.log('Failed to fetch hello', error);
	}

)

catch is roughly equivalent to the above. A subtle distinction is that, when calling then with two functions as arguments, only one will be called; with catch, both the then function and the catch function get called following a rejection.

fetch('/api/v1/hello')
.then(
	function(response) {
		response.text()
		.then(
			function(text) {
				console.log(text);
			}
		)
		.catch(
			function(error) {
				console.log('Failed to extract text from hello response', error);
			}
		)
	}
)
.catch(
	function(error) {
		console.log('Failed to fetch hello', error);
	}
)

Or, slightly more concisely:

fetch('/api/v1/hello')
.then(
	function(response) {
		return response.text();
	}
)
.then(
	function(text) {
		console.log(text);
	}
)
.catch(
	function(err) {
		console.log(err);
	}
)

Even more concisely using arrow functions:

fetch('/api/v1/hello')
.then(response => response.text())
.then(text => console.log(text))
.catch(err => console.log(err));

The response returned by fetch() is not itself JSON, but a Response object providing the methods: clone(), redirect(), arrayBuffer(), formData(), blog(), text(), and json(). Call text() on the response to get the data as a text value, for example.

The response object also has the Response.ok and Response.status. Response.ok is true of the HTTP response code is in the range 200–299. Response.status contains the specific numeric HTTP response code.

It’s possible to construct the Request we send by suppling an init object as a second optional arugument to fetch(). This can set the method (e.g., GET or POST), credentials (cookies), headers, and so forth.

var request = new Request('/api/v1/foo', {
	method: 'POST',
	mode: 'cors',
	redirect: 'follow',
	headers: new Headers({
		'Content-Type': 'text/plain'
	})
});

fetch(request).then(function() { /* Handle the response. */ });