I have a web service that returns an SseEmiter, i am using it to program a loading bar but...Well, the method to receive it is this:
static async synchronize(component: Vue) {
let xhr = new XMLHttpRequest();
xhr.open('PATCH', 'myUrl.com');
xhr.responseType = "text"
xhr.setRequestHeader('Authorization', 'mySessionToken')
xhr.setRequestHeader("Content-Type", "text/event-stream")
xhr.onload = function () {
if (this.status >= 200 && this.status < 300) {
resolve(xhr.response)
} else {
reject({status: this.status, statusText: xhr.statusText})
}
}
xhr.onerror = function () {reject({status: this.status, statusText: xhr.statusText})}
xhr.onreadystatechange = function() {if (xhr.readyState == XMLHttpRequest.DONE) { alert(xhr.responseText) }}
xhr.onprogress = function(onEvent) {
console.log(xhr.response)
}
xhr.send()
}
Right now this works, but...The xhr.response
data returns this as a string.
data:"{ hello: '1' }"
data:"{ hello: '2' }"
data:"{ hello: '3' }"
data:"{ hello: '4' }"
data:"{ hello: '5' }"
data:"{ hello: '6' }"
data:"{ hello: '7' }"
data:"{ hello: '8' }"
data:"{ hello: '9' }"
data:"{ hello: '10' }"
data:"{ hello: '11' }"
data:"{ hello: '12' }"
data:"{ hello: '13' }"
data:"{ hello: '14' }"
data:"{ hello: '15' }"
data:"{ hello: '16' }"
data:"{ hello: '17' }"
data:"{ hello: '18' }"
data:"{ hello: '19' }"
//And so on until it reach 100.
This doesn't seem like a problem, but what happens is that on each emitted event, the entire state history is returned, getting larger with each value:
In short, is there any way to get ONLY the last value of such response? Json.parse()
is throwing errors, probably because the response is not formatted as json.
I could share some code of the web service where the SseEmitter comes from, but i don't think it's necessary to resolve this. Thanks for any help!
CodePudding user response:
Since the string is a JSON string you could parse that
const data = JSON.parse(xhr.response.replace("data:", "")) // { hello: 'xx' }
CodePudding user response:
Why are you using XMLHttpRequest
to consume SSE, instead of EventSource
?
Anyway, assuming you have a good reason, you need to do a bit more work.
First make a new function to get the latest data block; we will be giving this a JS object, not a string:
function onData(d){
console.log(d)
}
Then, the basic idea is to keep track of what we have processed so far, and just slice off the new bit:
let lastLen = 0
xhr.onprogress = function(onEvent) {
const s = xhr.response.slice(lastLen 5)
lastLen = xhr.response.length
onData(JSON.parse(s))
}
If we did xhr.response.slice(lastLen)
we just get just the last "data: {...}" block (possibly with leading and/or trailing whitespace), so the 5
is to get us past the "data:" part.
This is rather brittle. If there is leading whitespace that 5
will be wrong. You might also get two or more "data: {...}" blocks arrive together. In which case you need to do something like: const parts = xhr.response.slice(lastLen).split("\n\n")
then process each entry in parts
.
To do it properly you should study the EventSource standard, or the code of one of the polyfill libraries. Or simply use one of those polyfill libraries ;-)