The fetch method allows to track download progress.
Please note: thereâs currently no way for fetch to track upload progress. For that purpose, please use XMLHttpRequest.
To track download progress, we can use response.body property. Itâs a âreadable streamâ â a special object that provides body chunk-by-chunk, as it comes, so we can see how much is available at the moment.
Hereâs the sketch of code that uses it to read response:
// instead of response.json() and other methods
const reader = response.body.getReader();
// infinite loop while the body is downloading
while(true) {
// done is true for the last chunk
// value is Uint8Array of the chunk bytes
const {done, value} = await reader.read();
if (done) {
break;
}
console.log(`Received ${value.length} bytes`)
}
So, we loop, while await reader.read() returns response chunks.
A chunk has two properties:
doneâ true when the reading is complete.valueâ a typed array of bytes:Uint8Array.
To log the progress, we just need to count chunks.
Hereâs the full code to get response and log the progress, more explanations follow:
// Step 1: start the fetch and obtain a reader
let response = await fetch('https://api.github.com/repos/javascript-tutorial/en.javascript.info/commits?per_page=100');
const reader = response.body.getReader();
// Step 2: get total length
const contentLength = +response.headers.get('Content-Length');
// Step 3: read the data
let receivedLength = 0; // length at the moment
let chunks = []; // array of received binary chunks (comprises the body)
while(true) {
const {done, value} = await reader.read();
if (done) {
break;
}
chunks.push(value);
receivedLength += value.length;
console.log(`Received ${receivedLength} of ${contentLength}`)
}
// Step 4: concatenate chunks into single Uint8Array
let chunksAll = new Uint8Array(receivedLength); // (4.1)
let position = 0;
for(let chunk of chunks) {
chunksAll.set(chunk, position); // (4.2)
position += chunk.length;
}
// Step 5: decode into a string
let result = new TextDecoder("utf-8").decode(chunksAll);
// We're done!
let commits = JSON.parse(result);
alert(commits[0].author.login);
Letâs explain that step-by-step:
-
We perform
fetchas usual, but instead of callingresponse.json(), we obtain a stream readerresponse.body.getReader().Please note, we canât use both these methods to read the same response. Either use a reader or a response method to get the result.
-
Prior to reading, we can figure out the full response length from the
Content-Lengthheader.It may be absent for cross-domain requests (see chapter Fetch: Cross-Origin Requests) and, well, technically a server doesnât have to set it. But usually itâs at place.
-
Call
await reader.read()until itâs done.We gather response
chunksin the array. Thatâs important, because after the response is consumed, we wonât be able to âre-readâ it usingresponse.json()or another way (you can try, thereâll be an error). -
At the end, we have
chunksâ an array ofUint8Arraybyte chunks. We need to join them into a single result. Unfortunately, thereâs no single method that concatenates those, so thereâs some code to do that:- We create
new Uint8Array(receivedLength)â a same-typed array with the combined length. - Then use
.set(chunk, position)method to copy eachchunkone after another in the resulting array.
- We create
-
We have the result in
chunksAll. Itâs a byte array though, not a string.To create a string, we need to interpret these bytes. The built-in TextDecoder does exactly that. Then we can
JSON.parseit.
What if we need binary content instead of JSON? Thatâs even simpler. Instead of steps 4 and 5, we could make a blob of all chunks:
let blob = new Blob(chunks);
Once again, please note, thatâs not for upload progress (no way now), only for download progress.
Yorumlar
<code>kullanınız, birkaç satır eklemek için ise<pre>kullanın. EÄer 10 satırdan fazla kod ekleyecekseniz plnkr kullanabilirsiniz)