Strange javascript behavior - multiple active XMLHttpRequests at once? Long running scripts? - javascript

I'm attempting to issue two concurrent AJAX requests.
The first call (/ajax_test1.php) takes a very long time to execute (5 seconds or so).
The second call (/ajax_test2.php) takes a very short time to execute.
The behavior I'm seeing is that I /ajax_test2.php returns and the handler gets called (updateTwo()) with the contents from /ajax_test2.php.
Then, 5 seconds later, /ajax_test1.php returns and the handler gets called (updateOne()) with the contents from /ajax_test2.php still!!!
Why is this happening?
Code is here: http://208.81.124.11/~consolibyte/tmp/ajax.html

This line:-
req = new XMLHttpRequest();
should be:-
var req = new XMLHttpRequest();

As AnthonyWJones stated, your javascript is declaring the second AJAX object which first overwrites the req variable (which is assumed global since there is no var) and you are also overwriting the ajax variable.
You should separate your code i.e:
function doOnChange()
{
var ajax1 = new AJAX('ajax_test1.php', 'one', updateOne);
var ajax2 = new AJAX('ajax_test2.php', 'two', updateTwo);
}
function AJAX(url, action, handler)
{
if (typeof XMLHttpRequest == "undefined")
{
XMLHttpRequest = function()
{
try { return new ActiveXObject("Msxml2.XMLHTTP.6.0") } catch(e) {}
try { return new ActiveXObject("Msxml2.XMLHTTP.3.0") } catch(e) {}
try { return new ActiveXObject("Msxml2.XMLHTTP") } catch(e) {}
try { return new ActiveXObject("Microsoft.XMLHTTP") } catch(e) {}
throw new Error( "This browser does not support XMLHttpRequest." )
};
}
url = url + '?action=' + action + '&rand=' + Math.random()
var req = new XMLHttpRequest();
req.onreadystatechange = function() {
if (req.readyState == 4)
{
if (req.status == 200)
{
alert('' + handler.name + '("' + req.responseText + '") ')
handler(req.responseText)
}
}
}
req.open("GET", url, true);
req.send(null);
}
Regards
Gavin

Diodeus and Mike Robinson:
You guys didn't read my post fully. I know that one of the pages takes longer to execute than the other. That is the expected behavior of each page.
HOWEVER if you read my original post, the problem is that the callback for both pages ends up getting called with the HTML contents of the first page only.
AnthonyWJones and Gavin:
Thanks guys! That works like a charm! I guess I need to brush up on my Javascript!

Related

Parallel asyncronous XMLHttpRequests are only calling back once

I am issuing two API calls in parallel, asynchronously so I don't lock up the browser , and I am only receiving one callback.
Here is the code
/* Loop runs twice, from 0 to 1 */
for(var AccountIndex in walletForm.masterpublicKey){
/* Bunch of code, then... */
/* Construct an API call to Insight */
var xhr = new XMLHttpRequest();
xhr.open("GET", "https://insight.bitpay.com/api/addrs/" + stringOfAddresses + "/txs?from=0&to=100", true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
$scope.$apply(function () {
txListInsight.txs[AccountIndex] = ( JSON.parse(xhr.responseText));
/* Set semaphore */
txListInsight.txsReady[AccountIndex] = true;
parseTransactions(AccountIndex);
console.log(txList);
})
}
}
xhr.send();
}
I can even see the two requests in the Chrome Dev Console Network Tab and the responses are correct. Why am I only getting one callback and not two? Is my second callback overwriting the reference to the first one?
Why is there a library on the Internet called “AsyncXMLHttpRequest”? I am using AngularJS as well--shall I look into "promises"?
Another option would be to avoid the problem entirely by combining my two API requests into one, but I'm not sure what the character limit is.
I think explicitly invoking function with the current AccountIndex should work, notice the closure
var xhrs = {};
for(var AccountIndex in walletForm.masterpublicKey){
(function(AccountIndex) {
xhrs[AccountIndex] = new XMLHttpRequest();
xhrs[AccountIndex].open("GET", "https://insight.bitpay.com/api/addrs/" + stringOfAddresses + "/txs?from=0&to=100", true);
xhrs[AccountIndex].onreadystatechange = function() {
if (xhrs[AccountIndex].readyState == 4) {
$scope.$apply(function () {
txListInsight.txs[AccountIndex] = ( JSON.parse(xhrs[AccountIndex].responseText));
/* Set semaphore */
txListInsight.txsReady[AccountIndex] = true;
parseTransactions(AccountIndex);
console.log(txList);
})
}
}
xhrs[AccountIndex].send();
})(AccountIndex);
}

Simultaneous ajax calls

I'm trying to make 2 (or more) ajax calls simultaneously. I don't want to use jQuery, only pure JavaScript.
Most of the time, it works. data1 will output data from sample.com/ajax1 and data2 will output data from sample.com/ajax2, but sometimes (1 from 10) the second AJAX call will display result from the first one.
Why is this happening? Both AJAX requests are requesting data from the same domain, but from different URLs. Is there any way how to prevent this behavior?
Here is the script:
// First AJAX
var xmlhttp1;
// Second AJAX
var xmlhttp2;
if (window.XMLHttpRequest) {
xmlhttp1 = new XMLHttpRequest();
} else {
xmlhttp1 = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp1.onreadystatechange = function() {
if (xmlhttp1.readyState == 4 && xmlhttp1.status == 200) {
data = JSON.parse(xmlhttp1.responseText);
console.log('data1: ' + data);
}
}
xmlhttp1.open("GET", "http://sample.com/ajax1", true);
xmlhttp1.send();
if (window.XMLHttpRequest) {
xmlhttp2 = new XMLHttpRequest();
} else {
xmlhttp2 = new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp2.onreadystatechange = function() {
if (xmlhttp2.readyState == 4 && xmlhttp2.status == 200) {
data = JSON.parse(xmlhttp2.responseText);
console.log('data2: ' + data);
}
}
xmlhttp2.open("GET", "http://sample.com/ajax2", true);
xmlhttp2.send();
First of all, I recomment wrapping your xmlHttpRequest generation/handling in a function, so you don't duplicate code that much.
The problem you have there is that the data variable is global, so both ajax callbacks are using the same variable. You can fix it using the var keyword in both calls.
xmlhttp2.onreadystatechange = function() {
if (xmlhttp2.readyState == 4 && xmlhttp2.status == 200) {
var data = JSON.parse(xmlhttp2.responseText);
console.log('data2: ' + data);
}
}
Because you're not properly encapsulating data. The way you have it written, data is a global object, so it's available to be modified by either ajax call. Since ajax calls are asynchronous, this will lead to unpredictable values for data.
The problem is probably because you forgot to define data inside your function
anyway with this function you can create multiple requests and have more control over them..
var req={};
function ajax(a){
var i=Date.now()+((Math.random()*1000)>>0);
req[i]=new XMLHttpRequest;
req[i].i=i;
req[i].open('GET',a);
req[i].onload=LOG;
req[i].send();
}
function LOG(){
console.log(this.i,this.response);
delete req[this.i];//clear
}
window.onload=function(){
ajax('1.html');
ajax('2.html');
ajax('3.html');
}
uses xhr2... you need to modify the code to make it work with older browsers.

ajax success called before request has finished

at my whits end with this...
The problem I have is that around the same time (roughly 60-70 mins through the request) the success function is called for the ajax request, but the tomcat logs prove that the request is still running.
(perspective: the request is supposed to either return an empty string ("") or a html page (of errors) however when the success function is called, it returns a string of length 2 result = " " (2 white space characters)
I cannot get this to reproduce in chrome, only IE (which is a problem as we cater specificity for IE)
At first I though it was a jQuery issue as I have multiple polls running when the request is sent (to update jQuery progress bar info)
However...
When I test this with just one asynchronous ajax request (not jQuery) that calls a Java method that just loops for 3 hours printing out in the log each second, it always calls success around the 3600 second mark (still another 7200 seconds remaining).
Example code:
var http_request = false;
if (window.XMLHttpRequest) {
http_request = new XMLHttpRequest();
if (http_request.overrideMimeType) {
http_request.overrideMimeType('text/text');
}
}
else if (window.ActiveXObject) {
try {
http_request = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (e) {
try {
http_request = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (e) {}
}
}
if (!http_request) {
alert('Giving up: Cannot create an XMLHTTP instance');
return false;
}
url += "&random=" + Math.random();
http_request.open('GET', url, true);
http_request.onprogress = function () { };
http_request.ontimeout = function () { };
http_request.onerror = function () { };
http_request.onreadystatechange = function(){
if (http_request.readyState == 4) {
if (http_request.status == 200) {
result = http_request.responseText;
alert("success: ("+result.length+") ["+result+"]");
}
}
};
NOTE: this is not a time-out error (at least not an ajax one) as the ajax time-out options seem to work correctly and return time-out errors accordingly, but as I have said, its not a time-out error...the success function is called too early.
Hope somebody can help :)
Cheers,
Steve.
Update:
I've run the request with the network tab capturing and it shows that the result was aborted: here
Thanks #ArunPJohny, this has given me a new direction to look in. Is there some kind of "onAbort" ajax callback? As I would have thought this kind of response would be caught by the "error:" callback
Update 2:
I have since found my way to a number of SO topics, more notably: xmlhttprequest timeout / abort not working as expected?
Tim provides some useful links for catching the abort, but still no clue as to why its getting aborted.
follow-on: here

Form submit using Ajax

I need to submit the a form using Ajax with POST method.The code is as follows,
function persistPage(divID,url,method){
var scriptId = "inlineScript_" + divID;
var xmlRequest = getXMLHttpRequest();
xmlRequest.open("POST",url,true);
xmlRequest.onreadystatechange = function(){
alert(xmlRequest.readyState + " :" + xmlRequest.status);
if (xmlRequest.readyState ==4 || xmlRequest.status == 200)
document.getElementById(divID).innerHTML=xmlRequest.responseText;
};
xmlRequest.open("POST", url, false);
alert(xmlRequest.readyState);
xmlRequest.send(null);
}
but the form is not submitting(request is not executed or no data posted).How to submit the form using Ajax.
Thanks
There's a few reasons why your code doesn't work. Allow me to break it down and go over the issues one by one. I'll start of with the last (but biggest) problem:
xmlRequest.send(null);
My guess is, you've based your code on a GET example, where the send method is called with null, or even undefined as a parameter (xhr.send()). This is because the url contains the data in a GET request (.php?param1=val1&param2=val2...). When using post, you're going to have to pass the data to the send method.
But Let's not get ahead of ourselves:
function persistPage(divID,url,method)
{
var scriptId = "inlineScript_" + divID;
var xmlRequest = getXMLHttpRequest();//be advised, older IE's don't support this
xmlRequest.open("POST",url,true);
//Set additional headers:
xmlRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');//marks ajax request
xmlRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencode');//sending form
The first of the two headers is not always a necessity, but it's better to be safe than sorry, IMO. Now, onward:
xmlRequest.onreadystatechange = function()
{
alert(xmlRequest.readyState + " :" + xmlRequest.status);
if (xmlRequest.readyState ==4 || xmlRequest.status == 200)
document.getElementById(divID).innerHTML=xmlRequest.responseText;
};
This code has a number of issues. You're assigning a method to an object, so there's no need to refer to your object using xmlRequest, though technically valid here, this will break once you move the callback function outside the persistPage function. The xmlRequest variable is local to the function's scope, and cannot be accessed outside it. Besides, as I said before, it's a method: this points to the object directlyYour if statement is a bit weird, too: the readystate must be 4, and status == 200, not or. So:
xmlRequest.onreadystatechange = function()
{
alert(this.readyState + " :" + this.status);
if (this.readyState === 4 && this.status === 200)
{
document.getElementById(divID).innerHTML=this.responseText;
}
};
xmlRequest.open("POST", url, false);
alert(xmlRequest.readyState);//pointless --> ajax is async, so it will alert 0, I think
xmlRequest.send(data);//<-- data goes here
}
How you fill the data is up to you, but make sure the format matches the header: in this case 'content type','x-www-form-urlencode'. Here's a full example of just such a request, it's not exactly a world beater, since I was in the process of ditching jQ in favour of pure JS at the time, but it's serviceable and you might pick up a thing or two. Especially take a closer look at function ajax() definition. In it you'll see a X-browser way to create an xhr-object, and there's a function in there to stringify forms, too
POINTLESS UPDATE:
Just for completeness sake, I'll add a full example:
function getXhr()
{
try
{
return XMLHttpRequest();
}
catch (error)
{
try
{
return new ActiveXObject('Msxml2.XMLHTTP');
}
catch(error)
{
try
{
return new ActiveXObject('Microsoft.XMLHTTP');
}
catch(error)
{
//throw new Error('no Ajax support?');
alert('You have a hopelessly outdated browser');
location.href = 'http://www.mozilla.org/en-US/firefox/';
}
}
}
}
function formalizeObject(form)
{//we'll use this to create our send-data
recursion = recursion || false;
if (typeof form !== 'object')
{
throw new Error('no object provided');
}
var ret = '';
form = form.elements || form;//double check for elements node-list
for (var i=0;i<form.length;i++)
{
if (form[i].type === 'checkbox' || form[i].type === 'radio')
{
if (form[i].checked)
{
ret += (ret.length ? '&' : '') + form[i].name + '=' + form[i].value;
}
continue;
}
ret += (ret.length ? '&' : '') + form[i].name +'='+ form[i].value;
}
return encodeURI(ret);
}
function persistPage(divID,url,method)
{
var scriptId = "inlineScript_" + divID;
var xmlRequest = getXhr();
xmlRequest.open("POST",url,true);
xmlRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xmlRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencode');
xmlRequest.onreadystatechange = function()
{
alert(this.readyState + " :" + this.status);
if (this.readyState === 4 && this.status === 200)
{
document.getElementById(divID).innerHTML=this.responseText;
}
};
xmlRequest.open("POST", url, false);
alert(xmlRequest.readyState);
xmlRequest.send(formalizeObject(document.getElementById('formId').elements));
}
Just for fun: this code, untested, but should work allright. Though, on each request the persistPage will create a new function object and assign it to the onreadystate event of xmlRequest. You could write this code so that you'll only need to create 1 function. I'm not going into my beloved closures right now (I think you have enough on your plate with this), but it's important to know that functions are objects, and have properties, just like everything else:Replace:
xmlRequest.onreadystatechange = function()
{
alert(this.readyState + " :" + this.status);
if (this.readyState === 4 && this.status === 200)
{
document.getElementById(divID).innerHTML=this.responseText;
}
};
With this:
//inside persistPage function:
xmlRequest.onreadystatechange = formSubmitSuccess;
formSubmitSuccess.divID = divID;//<== assign property to function
//global scope
function formSubmitSuccess()
{
if (this.readyState === 4 && this.status === 200)
{
console.log(this.responseText);
document.getElementById(formSubmitSuccess.divID).innerHTML = this.responseText;
//^^ uses property, set in persistPAge function
}
}
Don't use this though, as async calls could still be running while you're reassigning the property, causing mayhem. If the id is always going to be the same, you can, though (but closures would be even better, then).
Ok, I'll leave it at that
This code can let you understand. The function SendRequest send the request and build the xmlRequest through the GetXMLHttpRequest function
function SendRequest() {
var xmlRequest = GetXMLHttpRequest(),
if(xmlRequest) {
xmlRequest.open("POST", '/urlToPost', true)
xmlRequest.setRequestHeader("connection", "close");
xmlRequest.onreadystatechange = function() {
if (xmlRequest.status == 200) {
// Success
}
else {
// Some errors occured
}
};
xmlRequest.send(null);
}
}
function GetXMLHttpRequest() {
if (navigator.userAgent.indexOf("MSIE") != (-1)) {
var theClass = "Msxml2.XMLHTTP";
if (navigator.appVersion.indexOf("MSIE 5.5") != (-1)) {
theClass = "Microsoft.XMLHTTP";
}
try {
objectXMLHTTP = new ActivexObject(theClass);
return objectXMLHTTP;
}
catch (e) {
alert("Errore: the Activex will not be executed!");
}
}
else if (navigator.userAgent.indexOf("Mozilla") != (-1)) {
objectXMLHTTP = new XMLHttpRequest();
return objectXMLHTTP;
}
else {
alert("!Browser not supported!");
}
}
take a look at this page. In this line: req.send(postData); post data is an array with values that should be posted to server. You have null there. so nothing is posted. You just call request and send no data. In your case you must collect all values from your form, as XMLHTTPRequest is not something that can simply submit form. You must pass all values with JS:
var postData = {};
postData.value1 = document.getElementById("value1id").value;
...
xmlRequest.send(postData);
Where value1 will be available on server like $_POST['value'] (in PHP)
Also there might be a problem with URL or how you are calling persistPage. persistPage code looks ok to me, but maybe I'm missing something. Also you can take a look if you have no errors in console. Press F12 in any browser and find console tab. In FF you may need to install Firebug extention. Also there you will have Network tab with all requests. Open Firebug/Web Inspector(Chrome)/Developer Toolbar(IE) and check if new request is registered in its network tab after you call persistPage.
I found you've invoked the
xmlRequest.open()
method twice, one with async param as true and the other as false. What exactly do you intend to do?
xmlRequest.open("POST", url, true);
...
xmlRequest.open("POST", url, false);
If you want to send asynchronous request, pls pass the param as true.
And also, to use 'POST' method, you'd better send the request header as suggested by Elias,
xmlRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencode');
Otherwise, you may still get unexpected issues.
If you want a synchronous request, actually you may handle the response directly right after you send the request, just like:
xmlRequest.open("POST", url, false);
xmlRequest.send(postData);
// handle response here
document.getElementById(scriptId).innerHTML = xmlRequest.responseText;
Hope this helps.

XMLHttpRequest object not being instantiated?

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);
...
}

Categories

Resources