Home > Software engineering >  How to parse and get the last received value from a XmlHttpRequest?
How to parse and get the last received value from a XmlHttpRequest?

Time:10-23

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:

enter image description here

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 ;-)

  • Related