anthumchris / fetch-progress-indicators Goto Github PK
View Code? Open in Web Editor NEWProgress indicators/bars using Streams, Service Workers, and Fetch APIs
Home Page: https://fetch-progress.anthum.com/
License: MIT License
Progress indicators/bars using Streams, Service Workers, and Fetch APIs
Home Page: https://fetch-progress.anthum.com/
License: MIT License
The content length reported by the HTTP header is the encoded length, whereas the length accumulated in the code is decompressed:
As a result, progress extends beyond 100%
As reported by @chrisdavidmills, image does not display in Firefox 59+, yet the image loads in the Service Worker as indicated in the progress bar.
Implement a Service Worker to intercept fetch
events to show progress bars for inline <img>
loading when src
attribute is set. All existing fetch()
calls should be removed and download progress monitoring should occur in the Service Worker
Development was lazily done on desktop, and these examples should be mobile-friendly/responsive and testable from phones
Progress indicators for file uploads (specifically indicators for Request
objects) would be a great example. At the time of writing, this doesn't seem possible yet, and the Request docs do indicate ReadableStream
may be used as a request body
// Post "hello" body. Browser sends and server responds with what it receives.
fetch(
new Request('https://dev.anthum.com/post-test/', {
body: 'hello',
headers: {
'content-type': 'text/plain'
},
method: 'POST'
})
)
.then(response => response.text())
.then(data => console.log(data))
.catch(error => console.error(error));
This may just be bad experimental code on my part or the browsers do not yet support it.
const stream = new ReadableStream({
start(controller) {
controller.enqueue(new TextEncoder().encode('hello'));
controller.close();
}
});
// Post ReadableStream body. Browser doesn't seem to send, and server responds with nothing received
fetch(
new Request('https://dev.anthum.com/post-test/', {
body: stream,
headers: {
'content-type': 'text/plain'
},
method: 'POST'
})
)
.then(response => response.text())
.then(data => console.log(data))
.catch(error => console.error(error));
Refactor with TransformStream
as described by @ricea:
The examples would be cleaner with TransformStream, but unfortunately no browser is shipping it yet. You could be really concise, something like (totally untested)
fetchEvent.respondWith(new Response(response.body.pipeThrough(new TransformStream({ transform(chunk, controller) { loaded += chunk.length; progress({loaded, total}); controller.enqueue(chunk); } })), response));
Hi thanks alot for the great work.Is it possible to puse the downlod ?
A SW could be used to show aggregate/total progress indicators for multiple assets that download individually as part dependencies for a PWA. Video games loading screens are good examples of this. For large downloads that would exceed a few seconds, it would make sense to load the SW first, refresh, and then show progress for multiple assets in transit.
This example should leverage HTTP2 to ensure that totals for all assets are gathered from the onset. Assumption is that multiple responses will come in before downloads complete, and that assumption should be tested with various "total asset" values (i.e. 5, 10, 20, 30). Various browsers and back-end Nginx server may effect the total files that could start to download, hence affecting the "total bytes" aggregate.
This recent originated from a recent StackOverflow question "Show progress while caching files using a service worker"
Demos do not work on Mobile Safari (versions 8 tested) and no errors show. Ensure that proper errors show if certain JS APIs are not yet implemented
Hi there -- thanks for these examples. One improvement I suggest is to show how to forward the headers from the original request:
return new Response(stream, { headers: response.headers });
In your fetch-basic example you read the response then "pipe" it to a new response:
...
return new Response(
new ReadableStream({
start(controller) {
const reader = response.body.getReader();
read();
function read() {
reader.read().then(({done, value}) => {
if (done) {
controller.close();
return;
}
loaded += value.byteLength;
progress({loaded, total})
controller.enqueue(value);
read();
}).catch(error => {
console.error(error);
controller.error(error)
})
}
}
})
);
})
.then(response => response.blob())
.then(data => {
status('download completed')
document.getElementById('img').src = URL.createObjectURL(data);
})
...
There is no need to perform this extra work:
const content = []; // <===
let bytesReceived = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
content.push(value); // <===
bytesReceived += value.byteLength;
}
document.getElementById('img').src = URL.createObjectURL(new Blob(content));
Full example:
const progressIndicator = document.getElementById('progress');
const { headers, body } = await fetch(
'https://fetch-progress.anthum.com/30kbps/images/sunrise-baseline.jpg'
);
const contentLength = headers.get('Content-Length');
if (contentLength === null) throw new Error('No Content-Length response header');
const totalBytes = parseInt(contentLength, 10);
progressIndicator.max = totalBytes;
if (body === null) throw new Error('No response body');
const reader = body.getReader();
const content = []; // <===
let bytesReceived = 0;
while (true) {
const { done, value } = await reader.read();
if (done) break;
content.push(value); // <===
bytesReceived += value.byteLength;
progressIndicator.value = bytesReceived;
}
document.getElementById('img').src = URL.createObjectURL(new Blob(content));
<progress id="progress" value="0"></progress>
<img id="img" alt="download-progress-img" src="data:,">
A "Reload" button currently exists to start the test over by reloading the entire page. This was a fast solution, and ideally a "Restart" or "Run Again" button should be implemented to re-start the test without full page reload.
ReadableStream.cancel()
can be used, which stops the network transfer and also currently shows partially loaded images, which is kind of cool for testing/observation load differences between Progressive and Baseline JPEG images
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.