Is there a way for JavaScript to verify that various ajax calls have been finished without relying on jQuery, and upon completion, run a specific block of code?

I have a good understanding of how to achieve this using jQuery, but I am curious about accomplishing the same thing using basic JavaScript without relying on jQuery. Both jQuery and JavaScript are closely related, so anything achievable in one can be done in the other with ease.

Could someone help quench my curiosity and guide me through the underlying concept?

Consider a common scenario where a user makes multiple AJAX calls and wants to run a piece of code only after both calls have completed. How can this be done?


Here is a basic example illustrating the scenario. I've made some attempts that didn't work as expected. The reason behind the failure is that JavaScript can handle only one AJAX call at a time. So, what is the correct approach to achieving this without jQuery?

// Code for retrieving HTML result for settings div within a log panel
// This page also inserts data into the database, which needs to finish before the second AJAX call runs
var xmlhttp;
if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest(); 
else  xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 
xmlhttp.open("POST","/scripts/post_login_panel_actions.php",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("settings_button_activate=1&display_image_url="+encodeURIComponent(settings_display_picture_url));

// Code for getting HTML result for login panel, containing the settings div
// This should run after the first call to render the new data from the database
var xmlhttp2;
if (window.XMLHttpRequest) xmlhttp2 = new XMLHttpRequest(); 
else xmlhttp2 = new ActiveXObject("Microsoft.XMLHTTP");
xmlhttp2.open("POST","/scripts/post_login_panel_actions.php",true);
xmlhttp2.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp2.send("settings_refresh_login_panel=1");     

// Once both calls are complete, render the new login panel and the settings div inside it
xmlhttp.onreadystatechange = function() { 
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200 && xmlhttp2.readyState == 4 && xmlhttp2.status == 200) {
        document.getElementById("login_panel").innerHTML = xmlhttp2.responseText;
        document.getElementById("login_panel_settings").innerHTML = xmlhttp.responseText;    
    }    
}

EDIT, after reading answers, comments, and trying to write some more code:

The answer provided by lleaff is excellent and versatile. It's so simple that you can easily implement it for different variables and scenarios. For those reasons, I have accepted it as the correct solution.

Additionally, here is some extra information that may help others who come across this page. On my own, I devised a secondary method (less refined and straightforward!) for controlling both:

  1. Determining the order in which the two AJAX calls are executed, particularly when the first call must complete before the second begins
  2. Ensuring the final function call occurs only after both AJAX calls finish

The challenge lies in JavaScript not naturally supporting actions like "trigger action X when variable Y changes." Thus, it might be difficult for some individuals to understand solutions suggesting keeping track of what has finished using global variables.

You can perform a check each time the onreadystatechange function detects a change in the ready state. The flaw in my initial implementation was checking the ready state of only one of the AJAX calls.

A less sophisticated (yet easy-to-follow!) alternative: Hopefully, this will provide additional assistance to those exploring this topic:

// Declare your AJAX variables~
var xmlhttp;
if (window.XMLHttpRequest) xmlhttp = new XMLHttpRequest(); 
else  xmlhttp = new ActiveXObject("Microsoft.XMLHTTP"); 
var xmlhttp2;
if (window.XMLHttpRequest) xmlhttp2 = new XMLHttpRequest(); 
else xmlhttp2 = new ActiveXObject("Microsoft.XMLHTTP");

// Step 1 (first AJAX call) starts executing here
xmlhttp.open("POST","/scripts/post_login_panel_actions.php",true);
xmlhttp.setRequestHeader("Content-type","application/x-www-form-urlencoded");
xmlhttp.send("settings_button_activate=1&display_image_url="+encodeURIComponent(document.getElementById("settings_display_picture_url").value));

// Step 2 (second AJAX call) - called later in the code!
function ajax_call_two() {
    xmlhttp2.open("POST","/scripts/post_login_panel_actions.php",true);
    xmlhttp2.setRequestHeader("Content-type","application/x-www-form-urlencoded");
    xmlhttp2.send("settings_refresh_login_panel=1");
    xmlhttp2.onreadystatechange = function() { 
        if (xmlhttp2.readyState == 4 && xmlhttp2.status == 200) {
            // When Step 2 is completed, proceed to Step 3
            stuff_to_do_after_all_ajax_calls_are_finished();    
        }    
    }   
}  

// Step 3 (perform actions after all AJAX calls finish) - called later in the code!
function stuff_to_do_after_all_ajax_calls_are_finished() {
    document.getElementById("login_panel").innerHTML = xmlhttp2.responseText;
    document.getElementById("login_panel_settings").innerHTML = xmlhttp.responseText;    
} 

// Invoke Step 2 once Step 1 finishes
xmlhttp.onreadystatechange = function() { 
    if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
        ajax_call_two();    
    }    
}

Answer №1

To handle the function passed to xmlhttp.onreadystatechange, simply assign it to the onreadystatechange property for each request.

Here is a more generalized approach to solving this issue:

function checkRequestsCompletion(requests) {
    return requests.every(function (request) {
        return request.readyState == 4;
    });
} 

function findUnsuccessfulRequests(requests) {
    var unsuccessful = requests.filter(function (request) {
         return request.status != 200;
    };
    return unsuccessful.length ? unsuccessful : null;
}

function handleRequestsCompletion(requests, callback) {   
    // Wrap callback in a function to verify all requests have completed     
    function sharedCallback() {
        if (checkRequestsCompletion(requests)) {
            callback(requests, findUnsuccessfulRequests(requests));
        }
    }

    // Assign the shared callback to each request's `onreadystatechange`
    requests.forEach(function (request) {
        request.onreadystatechange = sharedCallback;
    });
}

Use it like this:

handleRequestsCompletion([xmlhttp, xmlhttp2], function(requests, unsuccessful) {
    if (unsuccessful) { return; } // Abort if any requests failed

    document.getElementById("login_panel").innerHTML=requests[1].responseText;
    document.getElementById("login_panel_settings").innerHTML=requests[0].responseText;
});

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

What will occur if I use an XMLHttpRequest to request a file that is currently being downloaded?

My goal is to enhance links in a progressive manner using the PJAX style. My plan was to layer this on top of some regular prefetch <link>s: <link rel="prefetch" href="next.html"/> If the browser has already downloaded next.html, then the PJA ...

Creating a Typescript Library that Relies on Web Assembly?

I'm considering developing a TypeScript library for distributions that will rely on cephes. To enable the use of web assembly in browsers, we need to compile it in the following way: const cephes = require('cephes'); // Browser await cephe ...

A more efficient method to dynamically generate line plots using React

In my project, I have a React component that generates an SVG and draws lines on it. The code looks something like this: class Parent extends React.Component { constructor(props) { super(props); this.state = { dataset1: [[1, ...

Unable to access the internal function of an Angular controller

My goal is to invoke the inner function of an Angular controller. index.html <script type="text/javascript> $(document).ready(function() { $('#example').multiselect({ onChange: function(option, checked, ...

How can I target specific elements to toggle the active class using Javascript?

At the moment, I have a function that highlights my navbar menu items based on which one I clicked. However, the issue arises when there is a button inside my navbar receiving the highlight class upon clicking. I'm unsure of how to modify the JavaSc ...

Leverage the power of React by utilizing SVGR to easily integrate SVG files

Wondering if there's a way to bring in an SVG file from my public folder and use it as a React component like this: import { ReactComponent as MySvg } from '/assets/svg/mysvg.svg'; const MyComponent = () => { return ( <div> ...

Calls to debounced functions are postponed, with all of them running once the waiting timer is complete

Utilizing the debounce function to create a real-time search feature. Researching more on debouncing from https://css-tricks.com/debouncing-throttling-explained-examples/, it seems like the function should control the number of calls made. In my scenario ...

How to send a file to the browser in PHP without making it accessible to the web server

Is there a way to send a file located outside the web server root directory to the browser using PHP code and stream it out to the client? I need the file on the client side to open only when a user clicks on an "open" button or link. I plan to call a Jav ...

I need RxJs to return individual elements to the subscriber instead of an array when using http.get

I've been developing an Angular 2 app (RC5) with a NodeJS backend RESTful API integration. One specific route on the backend returns an array of 'Candidates': exports.list = function (req, res, next) { const sort = req.query.sort || null ...

Incorporating a radio button input into the parent component in a React application

The main component stores all the state data. Whenever there is a change in any input field, the function myChangeHandler is triggered and updates the state accordingly. myChangeHandler(event) { this.setState({ [event.target.name]: event.target.value }) ...

Is it possible to utilize a JSON file to input events into FullCalendar that can accommodate multiple users?

Here's how I'm integrating event information from my database with FullCalendar using PHP: Retrieve event information from the database. Organize the data into an array and customize formatting, colors, etc. Convert the array to JSON format usi ...

Aborting HTTP POST requests in IE due to error code 0x2ee2

Currently, I am utilizing angularjs for file uploads to my API. The page features a multi-file select option where users can choose one or multiple files. Upon selection, the page initiates calls to the api and uploads each file individually using requests ...

Error encountered when launching React application on IE11: SCRIPT1010- Identifier expected

Currently, I am developing a React application using webpack and babel. One issue that has cropped up is the appearance of "SCRIPT1010: Expected identifier" error messages when running it on IE11. To address this, I have managed to resolve most of these er ...

Tips on setting up AJAX/JSON with the Jackson library in Spring?

I previously inquired about how to refresh a specific section of a JSP page using Spring MVC on this platform. I attempted to send a value via AJAX to my Controller, but encountered difficulties. However, I managed to overcome it without utilizing JSON. ...

What is the best way to convert rows into columns within an array of values?

Below is the structure of my data: let information = [ { X:[ {title: 'Example1', quantity: 25}, {title: 'Example2', quantity: 35} ], Y:[ {title: 'Sample3', quantity: 45}, {title: 'Sample4 ...

Is MongoDB caching its collection in memory?

Utilizing mongoDB for storing a set of polygons and executing $geoIntersects queries to determine the polygon containing a specific point. My mongoose Schema is structured as follows: var LocationShema = mongoose.Schema({ name: String, geo: { ...

What is the best way to restore the original dimensions of images after they have been altered?

I have content displayed on a webpage with images, each image having a specific width or none specified. When a user clicks a button, the content is exported to a PDF file. To ensure the images fit correctly on the PDF page, I adjust their width and height ...

Is it possible to use Three.js to generate a single mesh that can replace multiple individual objects?

Check out my latest project at this link: maison.whiteplay.fr I'm currently working on creating a 3D PacMan game. In my code, I am using mesh to construct the level, but I'm facing performance issues due to the large number of individual bubble ...

Update the table following the Ajax response in Django

I have set up a filter form which is being sent through an ajax post request: $('#id_server').change(function(e) { $.post($("form").attr("action"), { action: 'filter', filter: $("#id_server").attr("name" ...

What is the best method for embedding JSON data into a script tag within a Zope Chameleon template?

Creating a pyramid/python application where the view callable passes in an array variable called data, structured as [[[x1,y1,z1,],...],[[v1,v2,v3],...]] In the view callable: import json jsdata = json.dumps(data) I aim to place this data within a java ...