MENU
AJAX- Fetch
A cleaner and newer interface, the Fetch API provides a more powerful and flexible feature set than XMLHttpRequest.
Note that you can cache the Fetch Request and Response using the Web Cache API to speed up repetitive loading.
Requesting JSON file
A basic fetch request() takes one parameter and returns a promise.
RESETRUNFULL
RESETRUNFULL
<!DOCTYPE><html>
<body>
<pre></pre>
<script>
fetch('/shared/students.json')
.then(response => response.json())
.then(data => document.querySelector("pre").appendChild(document.createTextNode(JSON.stringify(data,null,3))));
</script>
</body></html>
Second Parameter
You can pass in a second parameter to fetch() to precisely control how the request is sent.
The Body mixin defines the following methods to extract a body (implemented by both Request and Response):
- arrayBuffer()
- blob()
- json()
- text()
- formData()
RESETRUNFULL
<?php // /shared/answer.php ... (not meant to be changed in this demo)
$_POST = json_decode(file_get_contents("php://input"), true);
if ($_POST["answer"]==33) echo json_encode((object)array("result"=>"correct!"));
else if ($_POST["answer"] <33) echo json_encode((object)array("result"=>"too small"));
else echo json_encode((object)array("result"=>"too big"));
?>
<!DOCTYPE><html>
<body>
<pre></pre>
<script>
async function postData(url = '', data = {}) { // Default options are marked with *
const response = await fetch(url, {
method: 'POST', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'no-cache', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'same-origin', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json', // 'Content-Type': 'application/x-www-form-urlencoded'
},
redirect: 'follow', // manual, *follow, error
referrerPolicy: 'no-referrer',
/* no-referrer, *no-referrer-when-downgrade, origin, origin-when-cross-origin, same-origin, strict-origin, strict-origin-when-cross-origin, unsafe-url */
body: JSON.stringify(data) // body data type must match "Content-Type" header
});
return response.json(); // parses JSON response into native JavaScript objects
}
postData('/shared/answer.php', { answer: 42 })
.then(r => document.querySelector("pre").appendChild(document.createTextNode(r.result)));
</script>
</body></html>
FormData
You can extract all data from a form directly into FormData. Note that FormData must be fetched over the 'POST' method.
RESETRUNFULL
RESETRUNFULL
<?php // /shared/answer2.php
if ($_POST["answer"]==33) echo "correct!";
else if ($_POST["answer"] <33) echo "too small";
else echo "too big";
?>
<!DOCTYPE><html>
<body>
<form>
<input name="answer" type="number" value="50"/>
</form>
<button type="button" onclick="submit()">GUESS</button>
<p></p>
<script>
function submit(){
fetch("/shared/answer2.php", {method:"POST", body:(new FormData(document.querySelector('form')))})
.then(r=>r.text())
.then(r=>{
document.querySelector("p").innerHTML = r;
});
}
</script>
</body></html>
Files can be uploaded using an HTML <input type="file" multiple /> input element, FormData() and fetch().
RESETRUNFULL
RESETRUNFULL
<!DOCTYPE><html>
<body>
<script>
function upload(){
const formData = new FormData();
formData.append('title', 'My Vegas Vacation');
const photos = document.querySelector('input[type="file"][multiple]');
for (let i = 0; i < photos.files.length; i++) formData.append('photos', photos.files[i]);
fetch('/dummy/upload_photos', { method: 'POST', body: formData}) // demo only
.then(response => response.json())
.then(result => { console.log('Success:', result);})
.catch(error => { console.error('Error:', error);});
}
</script>
<input type="file" multiple/>
<button onclick="upload()" type="button">UPLOAD</button>
</body></html>
Asynchronous Iteration
The chunks that are read from a response are not broken neatly at line boundaries and are Uint8Arrays, not strings. The following example processes a text file line-by-line.
RESETRUNFULL
RESETRUNFULL
<!DOCTYPE><html>
<body>
<script>
async function* makeTextFileLineIterator(fileURL) {
const utf8Decoder = new TextDecoder('utf-8');
const response = await fetch(fileURL);
let reader = response.body.getReader()
let { value, done } = await reader.read();
value = value ? utf8Decoder.decode(value) : '';
const re = /\n|\r|\r\n/gm;
let startIndex = 0;
let result;
for (;;) {
let result = re.exec(value);
if (!result) {
if (done) break;
let remainder = value.substr(startIndex);
let { value, done } = await reader.read();
value = remainder + (value ? utf8Decoder.decode(value) : '');
startIndex = re.lastIndex = 0;
continue;
}
result = value.substring(startIndex, result.index);
if (result.trim()!="") yield result;
startIndex = re.lastIndex;
}
if (startIndex < value.length) { // last line didn't end in a newline char
yield value.substr(startIndex);
}
}
async function run() {
for await (let line of makeTextFileLineIterator("/shared/wishes_for_sons.txt")) alert(line);
}
</script>
<button onclick="run()" type="button">RECITE: Wishes for Sons</button>
</body></html>
Checking for Fetch Success
The Promise returned from fetch() won't reject on HTTP error status even if the response is an HTTP 404 or 500. Instead, it will resolve normally (with ok status set to false), and it will only reject on network failure or if anything (eg. CORS misconfiguration) prevented the request from completing.
RESETRUNFULL
RESETRUNFULL
<!DOCTYPE><html>
<body>
<script>
function run(){
fetch('/shared/flowers.jpg')
.then(response => {
alert("promise resolved normally");
if (!response.ok) throw new Error('Network response was not ok');
return response.blob();
})
.then(myBlob => { myImage.src = URL.createObjectURL(myBlob); })
.catch(error => {
alert('There has been a problem with your fetch operation:'+ error);
});
}
</script>
<button onclick="run()" type="button">FETCH</button>
</body></html>
The Request and Headers Objects
Request and Response bodies are one-use only. You can create a copy of the Request before the body is read.
RESETRUNFULL
RESETRUNFULL
<!DOCTYPE><html>
<body>
<img/>
<script>
const myHeaders = new Headers();
const myInit = {
method: 'GET',
headers: myHeaders,
mode: 'cors',
cache: 'default'};
const myRequest = new Request('/shared/testing.gif', myInit);
const anotherRequest = new Request(myRequest, myInit); // The fetch() method's parameters are identical to those of the Request() constructor.
fetch(anotherRequest)
.then(response => response.blob())
.then(myBlob => {
document.querySelector("img").src = URL.createObjectURL(myBlob);
});
</script>
</body></html>
You can pass in your Headers object. A good use case for headers is checking whether the content type is correct before you process it further.
const content = 'Hello World';
const myHeaders = new Headers();
myHeaders.append('Content-Type', 'text/plain');
myHeaders.append('Content-Length', content.length.toString());
myHeaders.append('X-Custom-Header', 'ProcessThisImmediately');
const myHeaders = new Headers({
'Content-Type': 'text/plain',
'Content-Length': content.length.toString(),
'X-Custom-Header': 'ProcessThisImmediately'
});
console.log(myHeaders.has('Content-Type')); // true
console.log(myHeaders.has('Set-Cookie')); // false
myHeaders.set('Content-Type', 'text/html');
myHeaders.append('X-Custom-Header', 'AnotherValue');
console.log(myHeaders.get('Content-Length')); // 11
console.log(myHeaders.get('X-Custom-Header')); // ['ProcessThisImmediately', 'AnotherValue']
myHeaders.delete('X-Custom-Header');
console.log(myHeaders.get('X-Custom-Header')); // [ ]
const myResponse = Response.error();
try {
myResponse.headers.set('Origin', 'http://mybank.com');
} catch (e) {
console.log('Cannot pretend to be a bank!');
}
fetch(myRequest)
.then(response => {
const contentType = response.headers.get('content-type');
if (!contentType || !contentType.includes('application/json')) throw new TypeError("Oops, we haven't got JSON!");
return response.json();
})
.then(data => {
/* process your data further */
})
.catch(error => console.error(error));
Aborting
The AbortController interface represents a controller object that allows you to abort one or more Web requests as and when desired.
To observe how aborting works below, go to full-screen mode, press F12 and navigate to 'Network'.
RESETRUNFULL
To observe how aborting works below, go to full-screen mode, press F12 and navigate to 'Network'.
RESETRUNFULL
<!DOCTYPE><html>
<body>
<button class="download" type="button">DOWNLOAD</button>
<button class="abort" type="button">ABORT</button>
<script>
var controller = new AbortController();
var signal = controller.signal;
var downloadBtn = document.querySelector('.download');
var abortBtn = document.querySelector('.abort');
downloadBtn.addEventListener('click', fetchVideo);
abortBtn.addEventListener('click', function() {
controller.abort();
alert('download aborted');
});
function fetchVideo() {
alert("download about to start");
fetch("/shared/sample.mp4", {signal}).then(function(response) {
console.log(response);
})
.catch(function(e) {
reports.textContent = 'Download error: ' + e.message;
});
}
</script>
</body></html>
The Fetch API requires Set-Cookie headers to be stripped before returning a Response object from fetch().