Mohammad Basit
Mohammad Basit

Reputation: 575

Execute A Function When All xmlHttpRequests Are Complete

BRIEF

I have a website which gets data from an API. So it makes some post and get requests to a server once the website is opened. Let us assume it makes 5 different xmlHttpRequests as soon as the page loads but we cannot exactly know the time each call takes from start to finish.

FOR EXAMPLE

Call to one endpoint takes 2 seconds to complete and the other one takes 1 second to complete. So that means after 2 seconds both the calls are said to be finished.

TO-DO

I want to execute a function after all the xmlHttpRequests are complete because I am using window.performance.getEntries() to fetch all the requests initiated by the webpage and send it to my server as I am doing a web analytics project in which I need to access the times taken by each network request and convert the data into a chart.

EXTRA-QUESTION OR HINT

Is there any event that can be attached to the window as shown below to listen for all the network calls and execute my piece of code when all are finished.

      window.addEventListener("load", function (event) {
       //execute some code here
      }

In the load event I am not able to capture all the requests by using performance.getEntries() because load fires before the ajax calls are finished.

As shown above I ask. Is there any trick or any event or anything in JavaScript by which we can wait untill all the XMLHTTPREQUESTS are finished and then execute some code.

Upvotes: 1

Views: 1867

Answers (2)

Mohammad Basit
Mohammad Basit

Reputation: 575

WORKING SOLUTION

We can track browser AJAX calls using the code below :

  const ajaxCallStatus = () => {
   window.ajaxCalls = 0; // initial ajax calls
   window.ajaxEndpoints = []; // keeping track of all ajax call urls (endpoints) 
   const origOpen = XMLHttpRequest.prototype.open;
   XMLHttpRequest.prototype.open = function() {
   window.ajaxCalls++;
   this.addEventListener('load', (event) => {
    if (!window.ajaxEndpoints.includes(event['currentTarget'].responseURL)) {
      window.ajaxEndpoints.push(event['currentTarget'].responseURL);
    }
    window.ajaxCalls--;
    switch (window.ajaxCalls) {
    case 0: // when ajax calls finish this runs
      addData(window.ajaxEndpoints);
    break;
    }
     });
    origOpen.apply(this, arguments);
    }
   }

ADD DATA FUNCTION - Used for sending data to some backend.

function addData(arr, origOpen) { 
   XMLHttpRequest.prototype.open = origOpen;
   axios.post('url', data)
  .then((res) => console.log(res))
  .catch((err) => console.log(err));
}

Upvotes: 0

MauriceNino
MauriceNino

Reputation: 6757

I would suggest you to wrap your requests in promises and then use Promise.all(...) like so:

const makeRequest = () => {
    return new Promise((resolve, reject) => {
        var xhttp = new XMLHttpRequest();
        xhttp.onreadystatechange = function() {
            if (this.readyState == 4 && this.status == 200) {
                resolve(xhttp.responseText); // On success, resolve the promise
            } else {
                reject(); // On failure, reject it
            }
        };
        xhttp.open("GET", "filename", true); // Needs to be set to your needs. This is just an example
        xhttp.send();
    });
}

// This waits until all promises are resolved
const [result1, result2] = await Promise.all(makeRequest(), makeRequest());

// do stuff here with your results
console.log(result1);
console.log(result2);

PS: Basic Ajax example is from here, but just swap it for yours and create parameters or similar to make the requests you actually need

OR

You could use a library like Axios for Ajax requests, which already returns Promises by default. Check it out here: https://github.com/axios/axios

Upvotes: 0

Related Questions