Fetch streams 很棒,但并不适合用来测量上传/下载进度
Part of my role at Mozilla is making sure we're focusing on the right features, and we got onto the topic of fetch upload streams. It's something Chrome has supported for a while, but it isn't yet supported in either Firefox or Safari.
我在 Mozilla 的部分职责是确保我们专注于正确的功能,我们谈到了 fetch 上传流。Chrome 已经支持了一段时间,但 Firefox 和 Safari 尚未支持。
I asked folks on various social platforms what they thought of the feature, and what they'd use it for. The most common answer by far was "to measure upload progress", but… using it for that will give inaccurate results, and may even lead to bad implementations in browsers.
我在多个 社交 平台上询问大家对这个特性的看法以及会用它来做什么。最常见的回答就是“用来测量上传进度”,但……把它用于此目的会得到不准确的结果,甚至可能导致浏览器实现上的不良设计。
Let's dig in…
让我们深入探讨……
Response streams
Response streams
Streaming responses have been available in all browsers for years now:
流式响应已在所有浏览器中可用多年:
const response = await fetch(url);
const reader = response.body.getReader(); while (true) { const { done, value } = await reader.read(); if (done) break; console.log(value);
} console.log('Done!');
This lets you get the response chunk-by-chunk, so you can start processing it as you receive it.
这让你可以逐块获取响应,从而在接收的同时开始处理。
This is even easier with async iterators:
使用 async 迭代器甚至更简单:
const response = await fetch(url); for await (const chunk of response.body) { console.log(chunk);
} console.log('Done!');
…but that isn't supported in Safari. I've proposed it for interop 2026.
……但 Safari 并不支持。我已经提议将其纳入 2026 年的互操作计划。
The chunks are Uint8Array
s, but you can use TextDecoderStream
to get the chunks as text.
这些分块是 Uint8Array
,但你可以使用 TextDecoderStream
把它们转成文本。
const response = await fetch(url);
const textStream = response.body.pipeThrough(new TextDecoderStream());
But they're not ideal for measuring download progress
但它们并不适合测量下载进度
You could try to measure download progress like this:
你可以尝试这样测量下载进度:
const response = await fetch(url);
const contentLength = Number(response.headers.get('Content-Length')) || 0;
let downloaded = 0; for await (const chunk of response.body) { downloaded += chunk.length; if (contentLeng...