Search code examples
javascriptxmlhttprequest

Update DOM with responses from several XMLHttpRequest


I am building a simple open source Chromium extension that retrieve some data from several urls and then update the DOM. I could find another way to do this than by adding the line to update the DOM inside the callback http1.onreadystatechange

My XMLHttpRequest requests were often stuck on http1.readyState = 3 so I have added a 3rd parameter to http1.open("GET"); to make the request synchronous like this:

http1.open("GET", url, false);

But I am still getting these errors:

results[1].join is not a function at XMLHttpRequest.http.onreadystatechange
annot read property 'join' of undefined at XMLHttpRequest.http.onreadystatechange

Even thought they don't prevent the script from running, I think this isn't the right way to do what I want. So here is my question: how to update the DOM with the responses from several XMLHttpRequest request? Let's say I need to retrieve and compare all the data before updating the DOM. Then is there a way to process all the data at once after we have retrieve all of them (cf my comment on the last line)?

Here is the relevant part of my script, the full script is available here:

  var urls = [
      ["https://www.cnrtl.fr/morphologie/" + keyword, "vtoolbar", "morf_sound"], //best for plural
      ["https://www.cnrtl.fr/synonymie/" + keyword, "syno_format"],
  ]
  // for test set keyword to any of this word :  hibou,  tribal, aller, lancer

  var resultdiv = document.getElementById("result")
  resultdiv.innerText = "requete en cours";
  var results = [];
  var errors = [];

  urls.forEach((item, index) => {
      var http = new XMLHttpRequest();
      http.onreadystatechange = function () {
          if (http.readyState == 4 && http.status == 200) {
              parser = new DOMParser();
              var ulr1response = parser.parseFromString(http.responseText, "text/html");
              if (index == 0) {
                   //retrieve the data needed, save then in a list and push this list to the main list result

              } else if (index == 1) {
                  //retrieve the data needed, save then in a list and push this list to the main list result
              }

              // update the DOM 
              if (results[1] == "") {
                  resultdiv.innerHTML = results[0].join(", ") + "</br></br>Pas de synonymes trouvés"
              } else {
                  resultdiv.innerHTML = "<b>" + results[0].join(", ") + "</br></br>Synonymes:</b></br>● " + results[1].join('</br>● ')
              }

          } else {
              errors.push(index);
              resultdiv.innerText = "Erreur: " + index + " " + http.readyState + "  " + http.status;
          }
      }
      http.open("GET", item[0], false);
      http.send(null); // null = no parameters
  });

// it would be simplier if I could update the DOM here and not in  http.onreadystatechange

Solution

  • If you want to execute some code once all requests have succeeded, you can try using Promise.all together with Fetch.

    let keyword = "beaucoup";
    let parser = new DOMParser();
    
    let urls = [
      ["https://www.cnrtl.fr/morphologie/" + keyword, "vtoolbar", "morf_sound"], //best for plural
      ["https://www.cnrtl.fr/synonymie/" + keyword, "syno_format"]
    ];
    
    let fetchPromises = urls.map(
      item => fetch(item[0]).then(
        response => parser.parseFromString(response.text(), "text/html")
      )
    );
    
    Promise.all(fetchPromises).then(
      results => {
        // code in here executes once all fetchPromises have succeeded
        // "results" will be an array of parsed response data
        console.log(results);
      }
    ).catch(console.error);