I'm building a progress bar for some long-running server-side tasks (up to a few minutes), and I'd like a way to display the progress of the task. I could use WebSockets or poll on intervals, but I don't want to keep track of each task. Instead, I'd like to use long-polling and write progress updates to the stream.
Here is a demo of what the route should look like on the server
app.get('/test', (req, res) => {
let num = 0;
const interval = setInterval(() => res.write(num++ + ' '), 300);
setTimeout(() => {
clearInterval(interval);
res.send();
}, 5000);
});
Doing cURL on that endpoint with -N
works perfectly, however, I'm having some issues when it comes to implementing this in the browser.
I tried with fetch like this:
const response = await fetch(url);
const reader = response.body.getReader();
const decoder = new TextDecoder();
while (true) {
const { done, value } = await reader.read();
if (done)
break;
console.log(decoder.decode(value));
}
This worked just dandy on Chrome, but not on firefox and as you can see, it's not supported here:
https://caniuse.com/mdn-api_windoworworkerglobalscope_fetch_streaming_response_body
However, I tried a different approach, this time with XHR
const xhr = new XMLHttpRequest()
xhr.open("GET", url)
xhr.onprogress = function () {
console.log(xhr.responseText);
};
xhr.send();
This works perfectly in Firefox, but in Chrome, the onProgress event only fires after the entire request has been processed. I've also tried with onReadyStateChange
, but that results in the same problem.
>_< How do I read this gosh darn data in chunks as it updates in either browser? I guess I could try Axios, but do I really need this?
EDIT: One thing it might be worth mentoining is that Chrome and Firefox seem to handle the fetch behavior differently. With Chrome, I can work with the fetch object before the fetch completes, so I do
const response = await fetch(url);
console.log("Preflight complete, fetch is not done though");
but in Firefox, the console log won't execute until the fetch resolves. This is why I think I can't work with the response body in Firefox, but I can with Chrome.
According to this GitHub issue:
https://github.com/ratpack/ratpack/issues/443#issuecomment-59621215
This is a Chrome/Webkit bug. Changing the Content-Type
of the request from anything other than text/plain
makes it work with XHR
on Chrome. So if I change the server response to
app.get('/test', (req, res) => {
let num = 0;
let interval = setInterval(() => res.write(num++ + ' '), 300);
// THIS IS A DUMB HACK TO GET CHROME TO PLAY NICE X_X
res.setHeader('Content-Type', 'text/html');
setTimeout(() => {
clearInterval(interval);
res.send();
}, 5000);
});
SHOCKINGLY, this also seems to fix the issue with fetch streaming in Firefox with unmodified flags. I think I'll go with the XHR
method for now, just because it's a bit more compaitble, HOWEVER, the fetch version is significanlty easier to work with given each new chunk is processed individually.