How do I manage multiple, overlapping XMLHttpRequests? - javascript

I'm working on my first AJAX project, and I've started by setting up the following functions:
function sendHTTPRequest(callback,filename,data) {
if (window.XMLHttpRequest) {
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) {
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
httpRequest.onreadystatechange = callback;
httpRequest.open('POST',rootAddress+filename, true);
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
httpRequest.send(data);
}
function exampleCallback() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
// successful, parse the XML data
} else {
// error
}
} else {
// not ready
}
}
This has worked well but I've now gotten to the point where I have more than one simultaneous HTTP request, and my single global httpRequest variable isn't cutting it. It seems to me I could use an array, and .push a new XMLHttpRequest onto the stack each time sendHTTPRequest() is called, but how can I tell my various callback functions which item in the stack to parse? Or is the a better way to handle this process? (I'm using these to handle requests to different pages, with results that need to be parsed differently.)
Thanks!

Use a local variable and a per-request callback, which in turn calls the given callback. The changes required are surprisingly small; see ** lines:
function sendHTTPRequest(callback,filename,data) {
var httpRequest; // ** Local variable
if (window.XMLHttpRequest) {
httpRequest = new XMLHttpRequest();
} else if (window.ActiveXObject) {
httpRequest = new ActiveXObject("Microsoft.XMLHTTP");
}
// ** Callback specific to *this* request
httpRequest.onreadystatechange = function() {
if (httpRequest.readyState === XMLHttpRequest.DONE) {
if (httpRequest.status === 200) {
// successful, call the callback
callback(httpRequest);
} else {
// error, call the callback -- here we use null to indicate the error
callback(null);
}
} else {
// not ready
}
};
httpRequest.open('POST',rootAddress+filename, true);
httpRequest.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
httpRequest.send(data);
}
function exampleCallback(xhr) {
if (xhr) {
// successful, parse the XML data, for instance
var x = xhr.responseXML;
} else {
// not ready
}
}
You could have it give the callback more information (for instance, the filename and data arguments).
If you use promises, you could have sendHTTPRequest return a promise instead of accepting a direct callback.

httpRequest = new XMLHttpRequest();
Don't use a global. Use a local variable.
if (httpRequest.readyState === XMLHttpRequest.DONE) {
Don't use a global. Event handlers are called in the context of the object on which they fire. Use this.

Related

Javascript redirect on 404 error

There is an external website which contains an html document for each day of the year, and I am trying to automatically load the document for the current date. However, the urls are not consistent across every document. Some of them end with .html while others end with .htm, and some of them might have a letter appended to the filename.
What I want to do is attempt to load a url, and if it results in a 404 error, try loading a different url.
For example, I might do window.location.replace(baseurl+'.htm');
and if that results in a 404 error, attempt window.location.replace(baseurl+'.html'); and if that results in a 404, try something else.
How can I determine when the server sends a 404 error and attempt to load a new url?
Here's a function where you can pass in extensions to try, and it returns the first valid URL to a callback:
var request = new XMLHttpRequest();
var extensions = ['.html', '.htm'];
var count = 0;
var baseurl = 'http://blah.com/file';
function validUrl(baseurl, cb) {
request.open('GET', baseurl + extensions[count], true);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
cb(baseurl + extensions[count]);
} else {
count++;
if (count === extensions.length) {
cb(false);
} else {
validUrl(baseurl, cb);
}
}
}
};
request.send();
}
// usage:
validUrl(baseurl, function(url) {
if (url) {
// use the valid url
} else {
console.log('no valid urls');
}
});
As #Siguza said you can persist as an asynchronous request and validate it's status.
I suggest you to create a function which calls an AJAX and handle it's status returning to you if it's OK or not as a boolean. Something like:
// I'm building an function to validate the URL
function checkValidURL(url) {
var xmlhttp;
if (window.XMLHttpRequest) {
xmlhttp = new XMLHttpRequest();
} else {
// code for older browsers
xmlhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
// Callback
xmlhttp.onreadystatechange = function() {
if (this.status == 200) {
return true; // OK
} else { // or -> } else if (this.status == 404) {
return false; // Ops, something happen..
}
};
// AJAX configuration
xmlhttp.open("GET", url, false); // <- "false" for a synchronous request
xmlhttp.send();
}
Simple example of use:
if (!checkValidURL("http://www.my_url")) {
console.log("invalid URL");
// Call or check other URL
} else {
// It's OK, proceed with the business rules
}
Functions can wait AJAX requests by setting asynchronous "false", like: Is there any way to wait for AJAX response and halt execution?

AJAX issue returning the value

I am using a javascript function that calls another javascript function (zConvertEmplidtoRowid) that uses an ajax call that runs a query to return data in a variable (rowid). My problem is I don't know how to return the data to the original function.
Here is a snippet of the original function calling the ajax function (zConvertEmplidtoRowid)
var rowid = zConvertEmplidtoRowid(emplid);
//the alert should show what the query via ajax returned
alert(rowid);
zEmployeePortrait(emplid, ajaxlink);
}
And here is the ajax function...I imagine somewhere in here I need to place the return, but I've never used ajax before, so I'm not sure.
function zConvertEmplidtoRowid(emplid, ajaxlink, callback) {
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 or XMLHTTP.');
};
}
var request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
var rowid = request.responseText;
callback(rowid);
}
}
var ajax_link = ajax_link + "?emplid=" + emplid;
request.open('GET', ajax_link);
request.send();
}
As #epascarello pointed out, the ajax call is asynchronous and the code you have written is expecting the call to return in a synchronous way.
You have two options:
1) Make the ajax call synchronous (I highly recommend not to take this route).
2) Pass a callback function as a parameter to the function making the ajax call and then invoke the callback function once the call returns.
e.g:
function zConvertEmplidtoRowid(emplid, ajaxlink, callback) { //Added a callback function parameter
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 or XMLHTTP.');
};
}
var request = new XMLHttpRequest();
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
var rowid = request.responseText;
//now you invoke the callback passing the rowid as argument
callback(rowid);
}
}
var ajax_link = ajax_link + "?emplid=" + emplid;
request.open('GET', ajax_link);
request.send();
}
zConvertEmplidtoRowid(emplid, ajaxlink, function(rowId) {
alert(rowId);
zEmployeePortrait(emplid, ajaxlink);
});
As epascarello has implied in his comment, you need to make the javascript call synchronously in order to get a return value...
I tend to use jquery to assist with the call, but a quick google suggests you can do it your way by changing:
request.open('GET', ajax_link);
to:
request.open('GET', ajax_link, false);
and the response is then accessible through:
request.responseText
taken from here: https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/Synchronous_and_Asynchronous_Requests

Second request receives the first one that was still processing

I have a request system where two unrelated functions are making requests to my server. But the problem is the response is not correct let me explain what is happening step by step:
A background function makes a request to the server
Server processes task 1
A second unrelated background function makes a request to the server
Client recieves response of task 1
The second function recieves that response that was for the first function.
The first function never gets a response.
Now i don't know how to solve it, but i know i need to distinguish them separately so theres no conflictions here.
This is my current code that handles the request stuff:
function call_back(result,func){
if(typeof(func) != 'undefined' || func != false){
func(result);
} else {
return false;
}
}
function caller(url,cfunc){
if (window.XMLHttpRequest)
{
xmlhttp=new XMLHttpRequest();
}
else
{
xmlhttp=new ActiveXObject("Microsoft.XMLHTTP");
}
xmlhttp.onreadystatechange=cfunc;
xmlhttp.open("GET",url,true);
xmlhttp.send();
}
function call_file(url,func){ //handles html files (not json_encoded)
caller(url,function(){
if ( xmlhttp.readyState== 4 && xmlhttp.status== 200 ){
call_back(xmlhttp.responseText,func);
}
});
}
function call_data(url,func,JsonBool){ //handles json_encoded data
caller(url,function(){
if (xmlhttp.readyState==4 && xmlhttp.status==200){
call_back(JSON.parse(xmlhttp.responseText),func);
}
});
}
What can i do to my functions, for preventing this behaviour?
Here is an example of how you could structure your code - I have used this, it works, but it could be refined.
function Ajax(url, callback,args){
var xhttp = init();
xhttp.onreadystatechange = process;
function init() {
if(window.XMLHttpRequest)
return new XMLHttpRequest();
else if (window.ActiveXObject)
return new ActiveXObject("Microsoft.XMLHTTP");
}
function process() {
if (xhttp.readyState==4 && xhttp.status==200) {
if (callback) callback(xhttp.responseText,args);
else return xhttp.responseText;
}
}
this.Get=function(){
xhttp.open("GET", url, true);
xhttp.send(null);
}
}
To use it:
var url = '/someurl';
var ajax = new Ajax(url,yourCallback,parameters);
ajax.Get();
I believe DRobinson was talking about something like this but more robust. This should be a good example to get you started though.
It looks to me as though you're using a global/window variable for xmlhttp. If this is the case, certain parts of the second call will overwrite the first. Consider using an Object Oriented approach, or otherwise instantiating these as vars in different scopes.

javascript: wait for a return

I have this problem.
I have a function for example called functionA() that needs the results from another function called functionB().
var globalVar="";
function functionA(){
//...
functionB();
//here i have to use the global variable (that is empty because functionB isn't finished)
}
function functionB(){
//ajax request
globalVar=ajaxRequest.responseText;
}
How can I do to let the functionB finish befor continue with the execution of functionA?
Thanks!
This is the code:
var ingredientiEsistenti="";
function ShowInserisciCommerciale() {
getElementiEsistenti();
JSON.parse(ingredientiEsistenti);
}
function getElementiEsistenti(){
// prendo gli ingredienti esistenti.
var url = "http://127.0.0.1:8080/Tesi/Ingredienti";
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("POST", url, false);
xmlHttp.send(null);
xmlHttp.setRequestHeader("Content-type",
"application/x-www-form-urlencoded");
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4) // COMPLETED
{
if (xmlHttp.status == 200) // SUCCESSFUL
{
ingredientiEsistenti = xmlHttp.responseText;
} else {
alert("An error occurred while communicating with login server.");
}
}
};
}
You've got one of many options, that don't require an evil global variable:
Move the code you want to see executed to the onreadystatechange callback of the ajax request, that way, it won't get executed until you received a response
Redefine functionA, so that it takes a parameter that allows you to skip the first bit:
Make the request synchronous, not recommended, though
use a timeout/interval to check the readystate of the request manually (brute-force, not recommended either)
Perhaps there is some worker trickery that could do the trick, too, in your particular case
function functionA(skipDown)
{
skipDown = skipDown || false;
if (skipDown === false)
{
//doStuff
return functionB();//<-- call functionA(true); from the readystatechange callback
}
//this code will only be called if skipDown was passed
}
It is impossible to have a sleep/wait in JavaScript when the call is asynchronous. You need to use a callback pattern to make this action occur.
It is possible to make an XMLHttpRequest synchronous, but that can lead to other problems. It can hang the browser as it blocks all other actions from happening. So if you want to show a loading animation, it most likely will not execute.
You can make your AJAX request synchronous. https://developer.mozilla.org/en-US/docs/DOM/XMLHttpRequest/Synchronous_and_Asynchronous_Requests
var request = new XMLHttpRequest();
// Last parameter makes it not asnychronous
request.open('GET', 'http://www.mozilla.org/', false);
request.send(null);
// Won't get here until the network call finishes
if (request.status === 200) {
console.log(request.responseText);
}
However, that will block the UI while waiting for the server to respond, which is almost never what you want. In that case, you should use a callback to process results.
Here's an example using a callback without relying on a global variable. You should always run away from those
function ShowInserisciCommerciale( ) {
getElementiEsistenti(function(responseText) {
JSON.parse(responseText);
});
}
function getElementiEsistenti(successCallback){
var url = "http://127.0.0.1:8080/Tesi/Ingredienti";
var xmlHttp = new XMLHttpRequest();
xmlHttp.open("POST", url, false);
xmlHttp.send(null);
xmlHttp.setRequestHeader("Content-type",
"application/x-www-form-urlencoded");
xmlHttp.onreadystatechange = function() {
if (xmlHttp.readyState == 4) // COMPLETED
{
if (xmlHttp.status == 200) // SUCCESSFUL
{
successCallback(xmlHttp.responseText);
} else {
alert("An error occurred while communicating with login server.");
}
}
};
}

Getting undefined in javascript when calling ajax

function get_request(url) {
var request = new getXMLObject();
request.onreadystatechange = function () {
if (request.readyState == 4) {
alert(request.responseText);
var data = eval('(' + request.responseText + ')');
alert(data);
return data;
}
}
request.open("GET", url, true);
//alert(document.getElementById('energy').innerHTML);
request.send();
}
function loadjobs() {
var url = "loadjobs.php?tab=1&id=1111";
//var data=
//alert(check());
alert(get_request(url));
//alert(data);
}
When i m getting data in json format...i am gettin NULL in alert(get_request(url));
while i m getting in alert(data);
Help me
This is because the request in asynchronous . The get_request(url) function does to return anything and hence the null ( although I think it should be undefined and not null ) .
The onreadystatechange function gets called later in the time , when the AJAX request has been completed and the data is returned from the server and hence the alert there works .
This is a misunderstanding of how AJAX works. Ajax is asynchronous. The onreadystatechange function will be called after loadjobs(). The "return path" you are specifying can never work. get_request() will never be able to return the fetched value.
You have two options. Either make the script synchronous - this can be done but is not recommended because it can freeze the browser.
Or, better, handle everything you need to do inside the onreadystatechange callback.
Well, it's an asynchronous call. You will receive the data of request your after get_request has already returned. That means your request.onreadystatechange = function () will be executed long after alert(get_request(url)); is already finished. This means get_request will not be able to return any data from the AJAX call. That's what you have the request.onreadystatechange callback function for, to execute code at an undefined later time when you received the response.
The problem is that Ajax requests work asynchronously. So you can't return the data right away. The way you should do it is to specify a callback function which will handle the response data.
function handleJSON( data ) {
// ...
// do whatever you want to do with the data
}
ajax( "url/file.php?param=value", handleJSON );
////////////////////////////////////////////////////////////////////////////////
function getXmlHttpObject() {
var xmlHttp;
try {
xmlHttp = new XMLHttpRequest();
} catch (e) {
try {
xmlHttp = new ActiveXObject("Msxml2.XMLHTTP");
} catch (e) {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
}
}
return xmlHttp;
}
function ajax(url, onSuccess, onError) {
var xmlHttp = getXmlHttpObject();
xmlHttp.onreadystatechange = function () {
if (this.readyState == 4) {
// onError
if (this.status != 200) {
if (typeof onError == 'function') {
onError(this.responseText);
}
}
// onSuccess
else if (typeof onSuccess == 'function') {
onSuccess(this.responseText);
}
}
};
xmlHttp.open("GET", url, true);
xmlHttp.send(null);
return xmlHttp;
}​

Categories

Resources