W3Cschool
恭喜您成為首批注冊用戶
獲得88經(jīng)驗值獎勵
?fetch
? 方法允許去跟蹤 下載 進度。
請注意:到目前為止,fetch
方法無法跟蹤 上傳 進度。對于這個目的,請使用 XMLHttpRequest,我們在后面章節(jié)會講到。
要跟蹤下載進度,我們可以使用 response.body
屬性。它是 ReadableStream
—— 一個特殊的對象,它可以逐塊(chunk)提供 body。在 Streams API 規(guī)范中有對 ReadableStream
的詳細(xì)描述。
與 response.text()
,response.json()
和其他方法不同,response.body
給予了對進度讀取的完全控制,我們可以隨時計算下載了多少。
這是從 response.body
讀取 response 的示例代碼:
// 代替 response.json() 以及其他方法
const reader = response.body.getReader();
// 在 body 下載時,一直為無限循環(huán)
while(true) {
// 當(dāng)最后一塊下載完成時,done 值為 true
// value 是塊字節(jié)的 Uint8Array
const {done, value} = await reader.read();
if (done) {
break;
}
console.log(`Received ${value.length} bytes`)
}
await reader.read()
調(diào)用的結(jié)果是一個具有兩個屬性的對象:
done
? —— 當(dāng)讀取完成時為 ?true
?,否則為 ?false
?。value
? —— 字節(jié)的類型化數(shù)組:?Uint8Array
?。請注意:
Streams API 還描述了如果使用
for await..of
循環(huán)異步迭代ReadableStream
,但是目前為止,它還未得到很好的支持(參見 瀏覽器問題),所以我們使用了while
循環(huán)。
我們在循環(huán)中接收響應(yīng)塊(response chunk),直到加載完成,也就是:直到 done
為 true
。
要將進度打印出來,我們只需要將每個接收到的片段 value
的長度(length)加到 counter 即可。
這是獲取響應(yīng),并在控制臺中記錄進度的完整工作示例,下面有更多說明:
// Step 1:啟動 fetch,并獲得一個 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:獲得總長度(length)
const contentLength = +response.headers.get('Content-Length');
// Step 3:讀取數(shù)據(jù)
let receivedLength = 0; // 當(dāng)前接收到了這么多字節(jié)
let chunks = []; // 接收到的二進制塊的數(shù)組(包括 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:將塊連接到單個 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:解碼成字符串
let result = new TextDecoder("utf-8").decode(chunksAll);
// 我們完成啦!
let commits = JSON.parse(result);
alert(commits[0].author.login);
讓我們一步步解釋下這個過程:
fetch
,但不是調(diào)用 response.json()
,而是獲得了一個流讀取器(stream reader)response.body.getReader()
。請注意,我們不能同時使用這兩種方法來讀取相同的響應(yīng)。要么使用流讀取器,要么使用 reponse 方法來獲取結(jié)果。
Content-Length
header 中得到完整的響應(yīng)長度。跨源請求中可能不存在這個 header(請參見 Fetch:跨源請求),并且從技術(shù)上講,服務(wù)器可以不設(shè)置它。但是通常情況下它都會在那里。
await reader.read()
,直到它完成。我們將響應(yīng)塊收集到數(shù)組 chunks
中。這很重要,因為在使用完(consumed)響應(yīng)后,我們將無法使用 response.json()
或者其他方式(你可以試試,將會出現(xiàn) error)去“重新讀取”它。
chunks
—— 一個 Uint8Array
字節(jié)塊數(shù)組。我們需要將這些塊合并成一個結(jié)果。但不幸的是,沒有單個方法可以將它們串聯(lián)起來,所以這里需要一些代碼來實現(xiàn):chunksAll = new Uint8Array(receivedLength)
? —— 一個具有所有數(shù)據(jù)塊合并后的長度的同類型數(shù)組。.set(chunk, position)
? 方法,從數(shù)組中一個個地復(fù)制這些 ?chunk
?。chunksAll
中。但它是一個字節(jié)數(shù)組,不是字符串。要創(chuàng)建一個字符串,我們需要解析這些字節(jié)??梢允褂脙?nèi)建的 TextDecoder 對象完成。然后,我們可以 JSON.parse
它,如果有必要的話。
如果我們需要的是二進制內(nèi)容而不是字符串呢?這更簡單。用下面這行代碼替換掉第 4 和第 5 步,這行代碼從所有塊創(chuàng)建一個 Blob
:
let blob = new Blob(chunks);
最后,我們得到了結(jié)果(以字符串或 blob 的形式表示,什么方便就用什么),并在過程中對進度進行了跟蹤。
再強調(diào)一遍,這不能用于 上傳 過程(現(xiàn)在無法通過 fetch
獲?。?,僅用于 下載 過程。
另外,如果大小未知,我們應(yīng)該檢查循環(huán)中的 receivedLength
,一旦達到一定的限制就將其中斷。這樣 chunks
就不會溢出內(nèi)存了。
Copyright©2021 w3cschool編程獅|閩ICP備15016281號-3|閩公網(wǎng)安備35020302033924號
違法和不良信息舉報電話:173-0602-2364|舉報郵箱:jubao@eeedong.com
掃描二維碼
下載編程獅App
編程獅公眾號
聯(lián)系方式:
更多建議: