XMLHttpRequest send one after another - javascript

How do I send these XMLHttpRequests one at a time? Right now they are all firing immediately and if there are over six it locks up the server.
for (var i = 0; i <= document.getElementsByName("combobox")[0].value; i++) {
(function (i) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/grabdatafromeanotherpage.aspx", true);
xhr.send();
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
document.getElementById("txtHint").innerHTML = document.getElementById("txtHint").innerHTML + xhr.responseText;
}
}
})(0);
}

Just some untested ideas that may make Javascript gurus scream in agony, but hey, they haven't answered:
You could probably get it to work with (deprecated) synchronous calls, doing something like this:
for (var i = 0; i <= document.getElementsByName("combobox")[0].value; i++) {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/grabdatafromeanotherpage.aspx", false);
xhr.send(); // This will block until a request has been received, no need for a callback function
if (xhr.status == 200) {
document.getElementById("txtHint").innerHTML = document.getElementById("txtHint").innerHTML + xhr.responseText;
}
}
If you don't want to use synchronous calls, and you are sure the problem is on the server side (ie. the server can't handle this many almost-simultaneous requests) you could call request number i+1 from the callback of request i, but it would be very messy code.
You could also use the setTimeout() function to send the requests at intervals the server can handle, 500ms in this example:
for (var i = 0; i <= document.getElementsByName("combobox")[0].value; i++) {
setTimeout(myXHR, 500 * i)
}
function myXHR() {
var xhr = new XMLHttpRequest();
xhr.open("GET", "/grabdatafromeanotherpage.aspx", true);
xhr.onreadystatechange = function () {
if (xhr.status == 200 && xhr.readyState == 4) {
document.getElementById("txtHint").innerHTML = document.getElementById("txtHint").innerHTML + xhr.responseText;
}
};
xhr.send();
}
but the order of arrival of the responses would not be guaranteed.
There are probably better/hipper/more modern ways of doing all this with fetch/await.
If you have full control over the server, I would first try to persuade it somehow to accept and process the quick, successive requests; I find it a bit odd the server can't handle this.

Related

How to make callback run asynchronously?

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>

What is the best way to do long polling in AJAX requests in JavaScript without JQuery?

hi after searching in the net about how to use the long polling in JavaScript I ended up with three ways, they are mentioned here briefly,but they are implemented using JQuery. I am confused which one to use in case that the AJAX request that I send to the server is asynchronous GET request ,and I don't know how many time it could take.
here is an example AJAX request:
function asynchGETRequest(method,url){
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
console.log("ok");
}
};
xhttp.open(method, url, true);
xhttp.send();
return (xhttp.responseText);
}
var clientFunctions={
getAnswers : function(callback){
var res=asynchGETRequest("GET", "http://localhost:9000/answers");
callback(JSON.stringify(res));
}
}
clientFunctions.getAnswers (function(){
//do some code here after the ajax request is ended
});
can some one guide me please?
I think I found the solution here
function loadFile(sUrl, timeout, callback){
var args = arguments.slice(3);
var xhr = new XMLHttpRequest();
xhr.ontimeout = function () {
console.error("The request for " + url + " timed out.");
};
xhr.onload = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
callback.apply(xhr, args);
} else {
console.error(xhr.statusText);
}
}
};
xhr.open("GET", url, true);
xhr.timeout = timeout;
xhr.send(null);
}

foreach only sends last xhr request

I have a textarea where users can enter multiple URLs which in return will be used for an API request.
The issue I run into is that only the last URLs API request gets captured (sometimes multiple times).
$('.start').on('click',function()
{
var url_list = $("#url-list").val();
var urls = url_list.split("\n");
for (var i = 0, len = urls.length; i < len; i++) {
console.log("i is "+i)
var xhr = new XMLHttpRequest();
xhr.open('GET', urls[i], true);
xhr.send();
xhr.onreadystatechange = processRequest;
// send API request
function processRequest() {
if (xhr.readyState == 4 && xhr.status == 200) {
var response = JSON.parse(xhr.responseText);
console.log(response);
}
}
}
});
I don't see exactly where I am doing something wrong, I might be blind to it or just don't know any better. Any help would be appreciated. PS. fairly new to making API requests.
Welcome to closures.
The problems here are:
The loop could be finished before the first request starts, so it takes the last url in the array
In the callback you are referencing the same xhr object
You can try one of the different solutions here.
Replacing xhr with this in the callback is the fastest fix:
function processRequest() {
if (this.readyState == 4 && this.status == 200) {
var response = JSON.parse(this.responseText);
console.log(response);
}
}
See this fiddle for a running example.
You could update your code like this
var requestIndex = 0;
var urls = [];
$('.start').on('click',function()
{
// Reset request index
requestIndex = 0;
var url_list = $("#url-list").val();
urls = url_list.split("\n");
// Send Http request
sendRequest(urls, requestIndex);
});
// Send Http request
function sendRequest(urls, index) {
// Send API request
var xhr = new XMLHttpRequest();
xhr.open('GET', urls[index], true);
xhr.onreadystatechange = processRequest;
xhr.send();
}
// Process API request
function processRequest(e) {
if (e.target.readyState == 4 && e.target.status == 200) {
var response = JSON.parse(e.target.responseText);
console.log(response);
}
requestIndex++;
if (requestIndex < urls.length) {
sendRequest(urls, requestIndex);
}
}

xmlhttp.open multiple XML files

How would I go about fetching multiple XML files? I tried creating an array but that only opens the last file, and as I understand it xmlhttp.open is supposed to cancel any previous send. I tried modifying this which was the closest thing I could find, but my JavaScript knowledge is a bit to limited to adapt it.
This is the basic code I'm using to get one XML file.
if (window.XMLHttpRequest)
{ xmlhttp=new XMLHttpRequest();
}
xmlhttp.open("GET","myfile.xml",false);
xmlhttp.send();
xmlDoc=xmlhttp.responseXML;
var x=xmlDoc.getElementsByTagName("TAGNAME");
for (i=0;i<x.length;i++)
{ // Further parsing
}
Also is it possible to then display which file the parsed content comes from in my loop?
try this:
var arr = ["file1.xml", "file2.xml"],
cnt = 0, xhr = new XMLHttpRequest(), method = "GET";
function formatXml(file, xmlDoc) {
var x=xmlDoc.getElementsByTagName("TAGNAME");
console.log(file,x);
}
function getXml() {
xhr.open(method, arr[cnt], true);
xhr.onreadystatechange = function() {
if (xhr.readyState === XMLHttpRequest.DONE && xhr.status === 200) {
formatXml(arr[cnt], xhr.responseText);
cnt++;
if (cnt < arr.length) getXml(); // call again
}
};
xhr.send();
}
getXml(); // start it

Accessing data returned by ajax request on load

I've been working on a single page site in which data from a single json file is rendered variously in different sections - nothing displayed on load, but only upon click event. I learned a little about callbacks getting that wired up, but as I almost completed it I realized how flawed the concept was, so now I'm back to the drawing board.
My idea now is to make the ajax call onload, set the json result as a variable available to several functions. I thought it would go something like this:
window.onload = function() {
var myJsonData = function() {
var request = new XMLHttpRequest();
request.open("GET", "/json/someJsonData.json", true);
request.setRequestHeader("content-type", "application/json");
request.send(null);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
var myJsonString = JSON.parse(request.responseText);
var myJsonArray = myJsonString["Projects"];
return myJsonArray;
}
}
} // onreadystatechange
} // var myJsonData
myJsonData(); // *
console.log("myJsonArray: " + myJsonArray); // undefined
Or:
window.onload = function() {
myJsonData();
}
function myJsonData() {
var request = new XMLHttpRequest();
request.open("GET", "/json/someJsonData.json", true);
request.setRequestHeader("content-type", "application/json");
request.send(null);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
var myJsonString = JSON.parse(request.responseText);
var myJsonArray = myJsonString["Projects"];
alert("you are here (myJsonArray defined)");
alert("myJsonArray: " + myJsonArray);
console.log("myJsonArray: " + myJsonArray);
return myJsonArray;
}
}
} // onreadystatechange
} // var myJsonData
console.log("myJsonArray: " + myJsonArray); // undefined
Or:
window.onload = myJsonData;
// etc.
Of course myJsonArray is undefined either way. I'm obviously missing something fundamental about how to do this (or is this even a bad idea altogether?). Is there some way to pass a result as a callback when invoking an ajax request on load?
Can someone please enlighten me as to how to proceed from here, a skeletal example perhaps ? (p.s. still focusing on native js, not jQuery)
Many thanks in advance,
svs
There are some things wrong in the code. First the myJsonArray is local to the function myJsonData(). You have to define it globally. Second, the ajax request is async. You have to wait for the result before working with it. Here is a working example based on your code:
var myJsonArray; //Globally defined
window.onload = function() {
var outputData = function() {
console.log(myJsonArray);
}
var myJsonData = function() {
var request = new XMLHttpRequest();
request.open("GET", "someJsonData.json", true);
request.send(null);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
myJsonArray = JSON.parse(request.responseText);
outputData(); //Ouput when result is received
}
}
} // onreadystatechange
} // var myJsonData
myJsonData(); // *
}
Using Javascript promises could solve your problem here. Promises are native to ES6, although obviously the browser support isn't quite fully there yet. Regardless, here is how you could implement your code with promises:
window.onload = function () {
myJsonData().then(function (result) {
console.log("myJsonArray: " + result);
alert(result.one);
// do something else here
});
}
function myJsonData() {
return new Promise(function (resolve, reject) {
var request = new XMLHttpRequest();
request.open("GET", "http://echo.jsontest.com/key/value/one/two", true);
request.setRequestHeader("content-type", "application/json");
request.send(null);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
var myJsonString = JSON.parse(request.responseText);
//var myJsonArray = myJsonString["Projects"];
resolve(myJsonString);
}
}
} // onreadystatechange
});
} // var myJsonData
Here is a jsfiddle
From my experience and understanding.
When you are dealing with sync function and async function, always it is important to know which functions require data from the async callback function.
If the functions fully depend on the data received, it will a good coding practice to call these function in the loop
if (request.readyState === 4) {
if (request.status === 200) {
var myJsonString = JSON.parse(request.responseText);
// call the function which depend on the data received here.
}
}
I wouldn't recommend setting the async as false as it would just result in page freeze sometimes.

Categories

Resources