You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: 5-network/03-fetch-progress/article.md
+16-12
Original file line number
Diff line number
Diff line change
@@ -5,7 +5,7 @@ The `fetch` method allows to track *download* progress.
5
5
6
6
Please note: there's currently no way for `fetch` to track *upload* progress. For that purpose, please use [XMLHttpRequest](info:xmlhttprequest), we'll cover it later.
7
7
8
-
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.
8
+
To track download progress, we can use `response.body` property. It's `ReadableStream`-- a special object that provides body chunk-by-chunk, as it comes. Readable streams are described in the [Streams API](https://streams.spec.whatwg.org/#rs-class) specification.
9
9
10
10
Unlike `response.text()`, `response.json()` and other methods, `response.body` gives full control over the reading process, and we can count how much is consumed at any moment.
11
11
@@ -30,14 +30,18 @@ while(true) {
30
30
```
31
31
32
32
The result of `await reader.read()` call is an object with two properties:
33
-
-**`done`** -- true when the reading is complete.
33
+
-**`done`** -- `true` when the reading is complete, otherwise `false`.
34
34
-**`value`** -- a typed array of bytes: `Uint8Array`.
35
35
36
-
We wait for more chunks in the loop, until `done` is `true`.
36
+
```smart
37
+
Streams API also describes asynchronous iteration over `ReadableStream` with `for await..of` loop, but it's not yet widely supported (see [browser issues](https://github.com/whatwg/streams/issues/778#issuecomment-461341033)), so we use `while` loop.
38
+
```
39
+
40
+
We receive response chunks in the loop, until the loading finishes, that is: until `done` becomes `true`.
37
41
38
-
To log the progress, we just need for every `value` add its length to the counter.
42
+
To log the progress, we just need for every received fragment `value` to add its length to the counter.
39
43
40
-
Here's the full code to get response and log the progress, more explanations follow:
44
+
Here's the full working example that gets the response and logs the progress in console, more explanations to follow:
let receivedLength =0; //received that many bytes at the moment
53
57
let chunks = []; // array of received binary chunks (comprises the body)
54
58
while(true) {
55
59
const {done, value} =awaitreader.read();
@@ -84,21 +88,21 @@ Let's explain that step-by-step:
84
88
85
89
1. We perform `fetch` as usual, but instead of calling `response.json()`, we obtain a stream reader `response.body.getReader()`.
86
90
87
-
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.
91
+
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.
88
92
2. Prior to reading, we can figure out the full response length from the `Content-Length` header.
89
93
90
94
It may be absent for cross-domain requests (see chapter <info:fetch-crossorigin>) and, well, technically a server doesn't have to set it. But usually it's at place.
91
95
3. Call `await reader.read()` until it's done.
92
96
93
-
We gather response `chunks` in the array. That's important, because after the response is consumed, we won't be able to "re-read" it using `response.json()` or another way (you can try, there'll be an error).
97
+
We gather response chunks in the array`chunks`. That's important, because after the response is consumed, we won't be able to "re-read" it using `response.json()` or another way (you can try, there'll be an error).
94
98
4. At the end, we have `chunks` -- an array of `Uint8Array` byte 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:
95
-
1. We create `new Uint8Array(receivedLength)` -- a same-typed array with the combined length.
96
-
2. Then use `.set(chunk, position)` method to copy each `chunk` one after another in the resulting array.
99
+
1. We create `chunksAll = new Uint8Array(receivedLength)` -- a same-typed array with the combined length.
100
+
2. Then use `.set(chunk, position)` method to copy each `chunk` one after another in it.
97
101
5. We have the result in `chunksAll`. It's a byte array though, not a string.
98
102
99
-
To create a string, we need to interpret these bytes. The built-in [TextDecoder](info:text-decoder) does exactly that. Then we can `JSON.parse` it.
103
+
To create a string, we need to interpret these bytes. The built-in [TextDecoder](info:text-decoder) does exactly that. Then we can `JSON.parse` it, if necessary.
100
104
101
-
What if we need binary content instead of JSON? That's even simpler. Replace steps 4 and 5 with a single call to a blob from all chunks:
105
+
What if we need binary content instead of a string? That's even simpler. Replace steps 4 and 5 with a single line that creates a `Blob` from all chunks:
0 commit comments