前言
如何对请求进行进度控制,让用户知道请求进度或者响应进度。
Ajax有两种请求方式,分别为xhr
和fetch
,官方不再维护xhr
,而是推荐使用fetch
,下面分别介绍一下如何对请求进行进度控制。
一、xhr
现有请求函数
function request(options = {}) {
const { url, method = "GET", onProgress, data = null } = options
return new Promise((resolve) => {
const xhr = new XMLHttpRequest()
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === xhr.DONE) {
resolve(xhr.responseText)
}
})
xhr.open(method, url)
xhr.send(data)
})
}
响应进度
xhr
中可以监听progress
事件,loaded
和total
分别为当前请求字节数和总请求字节数,利用这两个值即可算出请求进度。
function request(options = {}) {
const { url, method = "GET", onProgress, data = null } = options
return new Promise((resolve) => {
const xhr = new XMLHttpRequest()
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === xhr.DONE) {
resolve(xhr.responseText)
}
})
xhr.addEventListener("progress", (e) => {
onProgress && onProgress({ loaded: e.loaded, total: e.total })
})
xhr.open(method, url)
xhr.send(data)
})
}
// 发送 GET 请求
request({
url: "http://192.168.1.18:5173/getdata",
data: JSON.stringify({
userId: 1,
}),
onProgress: (data) => {
console.log(data); // 得到loaded和total
// 展示进度条...
}
}).then((response) => {
console.log("Response:", response)
})
请求进度
几乎同上,只是监听方式改为:xhr.upload.addEventListener
function request(options = {}) {
const { url, method = "GET", onProgress, data = null } = options
return new Promise((resolve) => {
const xhr = new XMLHttpRequest()
xhr.addEventListener("readystatechange", () => {
if (xhr.readyState === xhr.DONE) {
resolve(xhr.responseText)
}
})
xhr.upload.addEventListener("progress", (e) => {
onProgress && onProgress({ loaded: e.loaded, total: e.total })
})
xhr.open(method, url)
xhr.send(data)
})
}
二、fetch
使用fetch
封装一个简单的请求
function request(options = {}) {
const { url, method = "GET", data = null } = options
return new Promise(async(resolve) => {
const resp = await fetch(url,{method,body:data})
const body = await resp.text()
resolve(body)
})
}
响应进度
从响应头中取出content-length
,这是响应数据的总字节数。
resp.body
是一个可读流
,通过getReader
拿到这个流的读取器,遍历读到每次的长度,以及是否完成。
function request(options = {}) {
const { url, method = "GET", onProgress, data = null } = options
return new Promise(async (resolve) => {
const resp = await fetch(url, { method, body: data })
const total = +resp.headers.get("content-length")
const decoder = new TextDecoder() // 文本解码器
let body = ""
const reader = resp.body.getReader()
let loaded = 0
while (true) {
const { done, value } = await reader.read()
if (done) break
loaded += value.length
body += decoder.decode(value)
onProgress && onProgress({ loaded, total })
}
resolve(body)
})
}
请求进度
fetch无法实现请求进度控制。
原因如下:
Request.body
本质上是ReadableStream可读流
,而可读流只能被一个人
读取,请求不同于响应,请求中对可读流进行读取的「人」是浏览器
。
W3C正在尝试一种方案,附带在ServiceWorker
中,BackgroundFetchManager
这套API是可以实现请求进度的监听,但是还处于试验阶段
,不能用于生产环境。