await fetch(url) возвращается, как только пришли заголовки. Тело ещё качается, и его можно читать по кускам через response.body.
const res = await fetch(url);
const total = +res.headers.get('Content-Length');
const reader = res.body.getReader();
const chunks = [];
let loaded = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
loaded += value.length;
progress.value = loaded / total;
}
Каждый reader.read() приносит следующий кусок: складываешь длины, делишь на размер файла из Content-Length и двигаешь полосу загрузки. Так умеют Chrome с 43-й версии, Firefox с 65-й и Safari с 10.1.
Если сервер сжимает ответ, в Content-Length окажется размер сжатого файла, а reader отдаёт распакованные байты, и прогресс уедет за 100%. Заголовка может и не быть, тогда показывай, сколько мегабайт уже скачано. Прогресс отправки так не посчитать, для неё по-прежнему берут XHR.
🔥 — если было полезно
@glo_academy | #js_day