Escher Escher - 20 days ago 6
Javascript Question

Promise is rejected prematurely multiple times before resolving

I'm making a call to an API and then trying to render a chart of the data returned:

function getFromAPI(url) {
return new Promise( (resolve, reject) => {
let xhr = new XMLHttpRequest();
xhr.onreadystatechange = () => {
if (xhr.readyState == 4 && xhr.status == 200) {
console.log("Resolving!");
jsonData = JSON.parse(xhr.responseText);
resolve(jsonData);
} else {
console.log("Rejecting!");
reject();
}
}
xhr.open("GET", url, true);
xhr.send();
});
}

getFromAPI(API_URL).then( (jsonData) => { drawChart(jsonData) });


When I load that script I get
Rejecting!
three times in the console before it resolves. The
reject
is also breaking the
.then
part (i.e. no chart for me!)

I take it the
onreadstatechange
event is firing a few times before we get to
readyState == 4
and
status == 200
. What exactly is going on and how do I avoid rejecting the promise prematurely?

Answer

The onreadystatechange is an event handler that is fired whenever the xhr readyState changes.

0   UNSENT  Client has been created. open() not called yet.
1   OPENED  open() has been called.
2   HEADERS_RECEIVED    send() has been called, and headers and status are available.
3   LOADING Downloading; responseText holds partial data.
4   DONE    The operation is complete.

As you can see there are 4 "not ready" states, and one "done". This accounts for the console log of reject and resolve that you see.

When the request is "done", reject or resolve the request according to the status:

xhr.onreadystatechange = () => {
  if (xhr.readyState !== 4) {
    return;
  }

  if (xhr.status === 200) {
    console.log("Resolving!");
    jsonData = JSON.parse(xhr.responseText);
    resolve(jsonData);
    return;
  }

  console.log("Rejecting!");
  reject();
}