I would like know how to handle multiple xhr requests with vanilla JS. I want to open multiple html templates and load the page when all of them are ready. when I use few xhr.open() requests it will only return 1 template:
var xhr = new XMLHttpRequest();
xhr.onload = function() {
if(xhr.status === 200){
storage.append(xhr.responseText);
}
}
function getAsync(url) {
xhr.open('get', url);
xhr.send();
}
getAsync('localhost:3000/template1.html');
getAsync('localhost:3000/template2.html');
I understand that .open() only works 1 at a time.
So is it possible to load all teamplates asynchronously or should I just load one after another in a synchronous matter? Also, I wonder,if I should create multiple xhr = new XMLHttpRequest() objects so that I could run multiple .open()?
Thank you
You are using one variable to define the xhr request, and using that variable twice, thus overriding the variable the second time. You need to create a loop and use let xhr; instead of var xhr as let has a block scope, so each instance in the loop will be independently defined.
i.e. something like
// Create iterable list of URLS
let urls = ['localhost:3000/template1.html', 'localhost:3000/template2.html'];
// Loop through URLs and perform request
for(let i=0; i<urls.length; i++) {
let xhr = new XMLHttpRequest();
xhr.onload = function() {
if(xhr.status === 200){
storage.append(xhr.responseText);
}
}
xhr.open('get', urls[i]);
xhr.send();
}
Related
I have a JavaScript function called getAandB which takes a callback. getAandB firstly gets value 'a' using ajax. It then invokes the callback with value 'a' as an argument. The callback gets value 'b' and console.logs both 'a' and 'b' to the console. so I get {"key":"a"} {"key":"b"} in the console.
I thought that the two ajax calls would happen simultaneously / asynchronously. However, they seem to run one after the other ie. synchronously.
The JavaScript code and the PHP code for the ajax requests is shown below:
index.html:
<script>
function getAandB(callback){
const xhr = new XMLHttpRequest();
xhr.open('GET', './ajax-a.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
callback(xhr.responseText)
}
}
xhr.send();
}
function callback(resultA){
const xhr = new XMLHttpRequest();
xhr.open('GET', './ajax-b.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
const resultB = xhr.responseText;
console.log(resultA, resultB);
}
}
xhr.send();
}
getAandB(callback);
</script>
ajax-a.php:
<?php
sleep(5);
$response = [
"key" => "a",
];
echo json_encode($response);
The code for ajax-b.php is the same as for ajax-a.php except the value of $response.key is b not a.
I thought that the above code would result in ajax calls being made simultaneously to get 'a' and 'b'. However if the PHP code sleeps for 5 seconds for both ajax-a.php and ajax-b.php, then it takes 10 seconds for the console.log to appear. If only one of the ajax-?.php scripts sleeps for 5 seconds then it takes 5 seconds for the console.log to appear.
How can I use callbacks to allow me to combine the results of ajax calls, as I have done here, but to make the individual calls happen simultaneously / asynchronously? Alternatively, is not possible to implement this with callbacks?
If you want the request to ajax-b to me made at approximately the same time as the request for ajax-a then you need to make the respective calls to xhr.send() at approximately the same time.
At the moment, the call to ajax-b's send() takes place as part of callback() which you only call after you have received the response to the request for ajax-a.
You then need to add additional logic to determine when you have received both responses so you log both bits of data at the same time (assuming you still want to do that).
A rough and ready way to do that, keeping to your current approach, would look something like this:
function getA(callback){
const xhr = new XMLHttpRequest();
xhr.open('GET', './ajax-a.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
callback(xhr.responseText)
}
}
xhr.send();
}
function getB(callback){
const xhr = new XMLHttpRequest();
xhr.open('GET', './ajax-b.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
const resultB = xhr.responseText;
callback(xhr.responseText)
}
}
xhr.send();
}
function getAandB() {
const data = [];
function callback(responseData) {
data.push(responseData);
if (data.length === 2) {
console.log(...data);
}
}
getA(callback);
getB(callback);
}
getAandB();
We have better tools for that these days though, thanks to promises and modern APIs (like fetch) which support them natively.
async function getAandB() {
const dataPromises = [
fetch("./ajax-a.php").then(r => r.text()),
fetch("./ajax-b.php").then(r => r.text())
];
const data = await Promise.all(dataPromises);
console.log(...data);
}
getAandB();
I tried to edit my question but 'the edit queue was full'.
It took me a while to understand #Quentin's answer but I finally realized it relies on the fact that both instantiations of the callback function are altering the same variable (I think that is called by reference and is the default situation with arrays). Given this, although the instantiations know nothing about each other, it is possible to know when both ajax calls have completed by checking to see if the data array has been updated twice. If it has then both must have completed and data can be consoled out.
There is no need for the getAandB function. This much simpler and less confusing code works exactly the same as Quentin's answer:
<script>
const data = [];
function getA(){
const xhr = new XMLHttpRequest();
xhr.open('GET', './ajax-a.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
data.push(xhr.responseText);
if (data.length === 2){
console.log(...data);
}
}
}
xhr.send();
}
function getB(){
const xhr = new XMLHttpRequest();
xhr.open('GET', './ajax-b.php', true);
xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xhr.onreadystatechange = function(){
if(xhr.readyState === 4 && xhr.status === 200){
data.push(xhr.responseText);
if (data.length === 2){
console.log(...data);
}
}
}
xhr.send();
}
getA();
getB();
</script>
I am sending an XMLHTTPrequest to a flask location like so...
var ourRequest = new XMLHttpRequest();
ourRequest.open("GET", "makeDiff")
diff = ourRequest.send();
console.log(diff);
The request then executes this Python code using Flask...
#app.route('/makeDiff')
def makeDiff():
diffNew = {"THIS IS A TEST": "HELLO", "TEST2": "HOLA"}
return json.dumps(diffNew)
However, after the python executes, the variable diff in the javascript is alway undefined. I don't know how to actually send the dictionary back to the javascript. There are many examples on how to do this with JQuery, but is it possible to do this with only javascript and no JQuery.
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function() {
if (xhr.readyState == XMLHttpRequest.DONE) {
alert(xhr.responseText);
}
}
xhr.open('GET', 'http://example.com/makeDiff', true);
xhr.send(null);
onreadystatechange callback is called when response arrive from python.
I have an ajax call that looks like this. My question is, with the callback function I can get the data out from myAjaxCall() to another function so I can work on the data. But is it possible to do multiple callbacks, so I can pass the data to two, three or four different functions that do all different things to the data?
Or do I have to do multiple ajax request for every function that I want to use?
function myAjaxCall() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'myJSON.json', true);
xhr.onload = function() {
if(this.status === 200) {
var data = JSON.parse(this.responseText);
callback && callback(data);
}
};
xhr.send();
}
You can just wrap the callbacks in another function and use that wrapper function instead:
function callback(data) {
callbackOne(data);
callbackTwo(data);
callbackThree(data);
. . .
}
If you need broader use of data than this, you'd need to resolve a Promise or something similar to get the data into a broader scope.
why not just call different functions with data as a parameter, sth. like this:
function myAjaxCall() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'myJSON.json', true);
xhr.onload = function() {
if(this.status === 200) {
var data = JSON.parse(this.responseText);
functionA(data);
functionB(data);
functionC(data);
}
};
xhr.send();
}
and inside your function do with data whatever you want to do:
functionA(data) {
// TODO do sth. with data
}
Another way could be this:
function myAjaxCall(callbacks) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'myJSON.json', true);
xhr.onload = function() {
if(this.status === 200) {
var data = JSON.parse(this.responseText);
callbacks && callbacks.forEach(function(callback) {
callback(data);
})
}
};
xhr.send();
}
// Where callback1 and callback2 are both functions
myAjaxCall([callback1, callback2]);
Note that I've changed the way of using myAjaxCall.
Also, if you don't mind using ES6, this would be a great option:
function myAjaxCall(...callbacks) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'myJSON.json', true);
xhr.onload = function() {
if(this.status === 200) {
var data = JSON.parse(this.responseText);
callbacks.length && callbacks.forEach(callback => callback(data));
}
};
xhr.send();
}
// Where callback1 and callback2 are both functions
myAjaxCall(callback1, callback2);
Note that in both cases I'm passing the callbacks to the ajax function, so no matter where that function is placed, it will have the callbacks available (I'm talking about scope). It's just not a good practice to define a function that uses user defined variables outside it's own scope.
i have a problem with my file js that call multiple http requests.
I have a button that call the function VisualizzaReport that is in my file visualizzaReport.js
Here is the function VisualizzaReport(select is the ID of the user)
function visualizzaReport(select){
reportUtente(select)
loadPianificazione(select)
}
Here the function reportUtente(select)
function reportUtente(select) {
var url = "../loadReportUtenteServlet?";
url += "type=perso_atti&value=" + select.value;
xmlhttp.onreadystatechange = handlerForReportUtente;
xmlhttp.open("GET", url);
xmlhttp.send("");
}
Here the function loadPianificazione(select)
function loadPianificazione(select) {
var url = "../loadPianificazione2Servlet?";
url += "type=pianificazioni&value=" + select.value;
xmlhttp.onreadystatechange = handlerForPianificazioneUtente;
xmlhttp.open("GET", url);
xmlhttp.send("");
}
my problem is that the function reportUtente is launched but has not effect because it seems that it is substitute by loadPianificazione function.
How can i call loadPianificazione only when reportUtente has finished his execution?
In your case I suggest you use either jQuery or AngularJs for this purposes.
jQuery example:
$.get('url1.com', function(){
$.get('url2.com');
})
You will request url2.com only when first request has finished.
You appear to be using a single, global xmlhttp variable.
Then you call two functions which do things with it. The second one will overwrite xmlhttp.onreadystatechange before the first request has finished so the second function you put there will be called for each request.
Don't do that. Create a new instance of XMLHttpRequest for each request, and keep it in a local scope so that it doesn't interfere with other instances.
function reportUtente(select) {
var url = "../loadReportUtenteServlet?";
url += "type=perso_atti&value=" + select.value;
var xmlhttp = new XMLHttpRequest(); // New instance here
xmlhttp.onreadystatechange = handlerForReportUtente;
xmlhttp.open("GET", url);
xmlhttp.send("");
}
You haven't shared handlerForReportUtente but it should look something like:
function handlerForReportUtente() {
alert(this.responseText); // Use `this` to get the right XHR object
}
I'm new to Ajax and this has me stumped. It's relatively simple but trips up on the onclick handler with the first xhr.open(). Firebug says it's not a function, my guess is the XMLHttpRequest object isn't being created, but I'm not sure. Anyone have any ideas?
Thanks in advance.
function init(){
function getXMLHTTP(){
var xhr = false;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
}
else if (window.ActiveXObject) {
xhr = new ActiveXObject("Microsoft.XMLHTTP");
}
return xhr;
}
function updatePage(theData){
$('results').innerHTML = theData; //TODO: pretty this up with mootools
}
var xhr = getXMLHTTP();
if (xhr) {
xhr.onreadystatechange = function(){
if (xhr.readyState == 4) {
if (xhr.status == 200) {
var theData = xhr.responseText;
updatePage(theData);
}
else{
alert("Error communicating to web service!");
}
}
}
$('submit_btn').onclick = function(xhr){
if ($('state').value != "") {
var theValue = $('state').value;
xhr.open("GET", "/inc/calc.php?state="+theValue, true); //Ajax 101: 3rd argument marks object as asynchronous
xhr.send(NULL);
}
else if ($('city').value != "") {
xhr.open("GET", "/inc/calc.php?city="+$('city').value, true);
xhr.send(NULL);
}
else if ($('county').value != "") {
xhr.open("GET", "/inc/calc.php?county="+$('county').value, true);
xhr.send(NULL);
}
else {
//the form is empty, or just down payment is filled. returns whole freakin' table.
xhr.open("GET", "/inc/calc.php", true);
xhr.send(NULL);
}
}
}
}
The problem with your code is the onclick function. You have put the xhr in the argument list to the function. Remember that when a function is called, the value of this variable is set by the caller. In this case it would be the event dispatcher, and it would probably set the xhr variable to an event object, which does not have an open function.
If you remove the xhr variable from the argument list of the onclick function, then it will look for the xhr variable in the parent scope, the global scope, and it will find it there, and it should work. I haven't tested it though.
I'm assuming you are using some kind of framework as well (judging by the frequent use of $, and the reference to mootools). This framework probably has an ajax function built in, as well as a cross browser event model. Try using it instead, you will run into a lot less problems.
When assigning the onclick handler you create a new function that takes a parameter called xhr:
$('submit_btn').onclick = function(xhr){
...
xhr.open("GET", "/inc/calc.php?state="+theValue, true);
...
}
The click on the button won't pass a XMLHttpRequest object the the handler, so xhr will not have an open() method. The global definition of xhr doesn't matter because it's shadowed by the local parameter definition.
Generally you should just generate a new local XMLHttpRequest object when you need it, not try to use a global one. For example use an onclick function that creates a new local XMLHttpRequest:
$('submit_btn').onclick = function(){
var xhr = getXMLHTTP();
xhr.open("GET", "/inc/calc.php?state="+theValue, true);
...
}