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:
- Determining the order in which the two AJAX calls are executed, particularly when the first call must complete before the second begins
- 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();
}
}