Progress Events
Progress events 用於描述資源加載的進度,主要由 AJAX 請求, <img>, <audio>, <video>, <style>, <link>…等外部資源的加載觸發。
主要包含以下幾種事件:
- abort: 外部資源中止加載時(比如使用者取消)觸發
- error: 由於錯誤導致外部資源無法加載時觸發
- load: 外部資源加載成功時觸發
- loadstart: 外部資源開始加載時觸發
- loadend: 外部資源停止加載時觸發, 發生順序在 error, abort, load…等事件的後面
- progress: 外部資源加載過程中不斷觸發
- timeout: 加載過時時觸發
比如下面這個例子:
image.addEventListener('load', (e) => {
image.classList.add('finished')
})
image.addEventListener('error', (e) => {
image.style.display = 'none'
})XHR 獲取當前請求進度
前面有提到 Progress event 其中包含 progress 事件是在外部資源加載過程中不斷觸發,所以我們要計算請求的當前進度就需要透過監聽 progress 事件來實現。
progress 事件的返回值是一個物件,物件中包括 loaded 和 total 這兩個值,loaded 代表當前請求的 byte 數,total 則是總共的 byte 數。
物件中還有一個 lengthComputable,如果 lengthComputable 為 true 代表該資源有可計算的長度,此時才能計算請求進度,如果為 false 就無法計算。
const xhr = new XMLHttpRequest()
xhr.addEventListener('progress', (e) => {
if (e.lengthComputable) {
console.log(e.loaded, e.total)
console.log('進度(%)', (e.loaded / e.total) * 100 + '%')
} else {
console.log('無法計算進度')
}
})簡單的例子如下:
var xhr = new XMLHttpRequest();
xhr.addEventListener('progress', (e) => {
console.log(e);
if (e.lengthComputable) {
console.log(e.loaded, e.total);
console.log('進度(%)', (e.loaded / e.total) * 100 + '%');
} else {
console.log('無法計算進度');
}
});
xhr.open('GET', 'https://pokeapi.co/api/v2/pokemon/ditto');
xhr.send();而如果要監聽上傳進度,只需要在 xhr.addEventListener 中間加上 upload 就可以,其餘寫法都一樣:
xhr.upload.addEventListener('progress', (e) => {
if (e.lengthComputable) {
console.log(e.loaded, e.total)
console.log('進度(%)', (e.loaded / e.total) * 100 + '%')
} else {
console.log('無法計算進度')
}
})axios
axios 本身有提供 onDownloadProgress 方法來獲取 progress event 物件,就不需要再自己寫事件監聽了:
axios({
method: "get",
url: "http://api.demo.com/todos",
onDownloadProgress(progressEvent) {
console.log(progressEvent)
}
})fetch 獲取當前請求進度
fetch 無法直接獲取請求進度,不過 fetch response 裡有一個 body,這個 body 是一個可讀流,可以藉由可讀流的 getReader() 方法拿到一個讀取器(reader),將讀取器中的 value 一直累加就是當前已加載的資料量(byte),直到 done 為 true 時代表請求完成。
至於總資料量,直接從 response 獲取 content-length 即可。
<div id="app">
<input type="button" name="btn" value="Submit" />
</div>const button = document.getElementsByName('btn')[0]
button.addEventListener('click', () => getProgress())
const getProgress = async () => {
const response = await fetch('https://jsonplaceholder.typicode.com/comments')
const total = +response.headers.get('content-length')
const reader = response.body.getReader()
let loaded = 0
while (1) {
const { done, value } = await reader.read()
if (done) break
loaded += value.length
console.log(loaded, total)
}
}上傳進度
fetch 目前無法計算上傳進度
Progress event 的 total 總是回傳 0
這是因為 response headers 沒有 Content-Length 而導致,可能是因為檔案被分塊發送或者使用gzip壓縮(Content-Encoding: gzip)。
解決方法就只有請後端在 response headers 加上 Content-Length 了。



![[Weekly] 11/06-11/12 本周紀錄 9 weekly-banner](https://www.may-notes.com/wp-content/uploads/2023/10/jazmin-quaynor-18mUXUS8ksI-unsplash-150x150.jpg)