先自问自答:fetch 原生做不到请求的进度监控。fetch 是基于 Promise 的,在Promise的世界里,它的结果只有成功或者失败,没有说成功了百分之几或者失败了百分之几的说法。
如果使用Fetch想实现进度监控功能,可能需要结合使用ReadableStream
、TransformStream
等Web Streams API,或者考虑使用第三方库来辅助实现。
一、简单实现进度监控
我们在发送请求之前,使用如FormData
或Blob
等对象来包装文件或数据,并通过监听这些对象在发送过程中的变化来间接获取上传进度。但这种方法并不直接由fetch
API 提供,实现起来较为复杂。
这里我们使用一个较新的API来试试。ReadableStream 支持流式处理数据,可以逐块读取数据,我们可以用它来实现一个文件流的进度。
ReadableStream
// fetch请求进度监控
async function fetchWithProgress(url, onProgress) {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`请求异常: ${response.status}`);
}
const reader = response.body.getReader();
let totalBytesReceived = 0;
const contentLength = parseInt(
response.headers.get("content-length") || 0
);
return new ReadableStream({
start(controller) {
function push() {
reader.read().then(({ done, value }) => {
if (done) {
controller.close();
return;
}
totalBytesReceived += value.length;
onProgress(totalBytesReceived, contentLength);
controller.enqueue(value);
push();
});
}
push();
},
});
}
// 示例
fetchWithProgress("https://example.com/large-file", (loaded, total) => {
console.log(
`下载进度: ${Math.round((loaded / total) * 100)}%`
);
}).then((stream) => {
// 处理stream
const writer = new WritableStream({
write(chunk) {
// 处理chunk
console.log(`接受chunk大小: ${chunk.length} bytes`);
},
});
stream.pipeTo(writer);
});
二、使用第三方库实现进度监控
- axios
import axios from 'axios';
axios.request({
method: 'GET',
url: 'http://localhost:3000/file',
responseType: 'stream',
onDownloadProgress: function(progressEvent) {
const percentCompleted = Math.round((progressEvent.loaded / progressEvent.total) * 100);
console.log(`下载进度: ${percentCompleted}%`);
}
}).then(response => {
// 处理响应
});
- fetch-progress
import fetchProgress from 'fetch-progress';
fetchProgress('http://localhost:3000/file', (progress) => {
console.log(`下载进度: ${progress.percent}%`);
}).then(response => {
// 处理响应
});
三、XHR的进度监控
如果不严格要求使用fetch
API,可以考虑使用XMLHttpRequest
,它原生支持上传和下载的进度事件,可以直接使用XMLHttpRequest
对象的onprogress
事件来跟踪上传进度。
// fetch请求进度监控
function downloadWithProgress(url, onProgress) {
const xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "blob";
// 监听下载进度
xhr.onprogress = function (event) {
if (event.lengthComputable) {
const percentComplete = Math.round(
(event.loaded / event.total) * 100
);
onProgress(percentComplete);
}
};
// 监听加载完成
xhr.onload = function () {
if (xhr.status === 200) {
console.log("下载完成!");
} else {
console.error("下载异常:", xhr.status);
throw new Error(`下载异常: ${xhr.status}`);
}
};
// 监听错误
xhr.onerror = function () {
console.error("请求错误!");
throw new Error("请求错误!");
};
// 监听超时
xhr.ontimeout = function () {
console.error("请求超时!");
throw new Error("请求超时!");
};
// 发送请求
xhr.send();
}
// 示例用法
downloadWithProgress("http://localhost:3000/file", (progress) => {
console.log(`下载进度: ${progress}%`);
});
总结:如果需要做请求的进度监控,在没有强制一点要使用Fetch的情况下,还是老老实实使用XHR吧,或者使用成熟的第三方请求库,比如axios等,它该有的功能都有了。