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?
Related
EDIT: REMEMBER TO ALWAYS CHECK YOUR NETWORK TAB FOR ERRORS, MY POSTED ANSWER ELABORATES THAT THIS WAS A STUPID QUESTION ON MY PART, THANKS epascarello
I'm writing some backend code for an HTML5 website and I can't get this very important function to work. The site will often need to dynamically pull PHP-generated XML files from a different server. I'm pretty sure that server has Access-Control-Allow-Origin set to allow requests from this server. I will verify with my boss but he's out for a couple days and honestly it's more likely something I'm doing wrong. Does anyone see something wrong with this code?
Notice I have an alert at the bottom before request.send(null) and after. .send() works fine and the function completes if I request an XML file from the same server, but if I request from the other I get the "before" alert but not the "after". I am also seeing no errors or other alerts displayed to me.
Notes:
There are some references to global variables and such in other .js files. I've determined that including them would be pointless and just clutter the question.
'cross' is just a boolean for whether the request is cross origin, to another server.
function createXmlHttpRequestObject(cross) {
if(cross === undefined) {
cross = false;
}
var xmlHttp;
if(cross) {
if(window.XDomainRequest) {
try {
xmlHttp = new window.XDomainRequest();
} catch(e) {
xmlHttp = false;
}
} else {
xmlHttp = new XMLHttpRequest();
}
} else {
if(window.ActiveXObject) {
try {
xmlHttp = new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {
xmlHttp = false;
}
} else {
try {
xmlHttp = new XMLHttpRequest();
} catch(e) {
xmlHttp = false;
}
}
}
if(!xmlHttp) {
alert ("Error: CREATING XMLHTTP REQUEST OBJECT FAILED");
} else {
return xmlHttp;
}
}
//receives xml object from file
function getXmlObject(URL) {
//if url contains any of site root, then it is not cross domain
var cross = false;
if(!URL.includes(url_home)) {
cross = true;
}
var request = createXmlHttpRequestObject(cross);
var response;
if(window.ActiveXObject || window.XDomainRequest) {
request.onload = function(){
response = request.responseXML;
};
request.open('GET',URL,false);
request.send(null);
} else {
request.open('GET',URL,false);
request.onreadystatechange = function(){
if(request.readyState == 4) {
if(request.status == 200) {
response = request.responseXML;
} else {
var warn = 'NOT FOUND\n' + URL;
response = warn;
alert(warn);
}
}
};
alert("before");
request.send(null);
alert("after");
}
return response;
}
Found in the network tab confirmation that there is in fact no Access-Control-Allow-Origin flag on the server I'm trying to access. I'm gonna have to talk to the right people and see if that can change. Still, if this code can be improved or if there's still an issue here I'm not seeing please point it out.
Is it possible for me to call a function then override the contents of the variable before actually running it?
So I have a function that basically pulls in my Git profile like this:
var GetGitInfo = function() {
var xhr = new XMLHttpRequest();
var gitURL = "https://api.github.com/users/myself/repos";
xhr.open("GET", gitURL);
xhr.send(null);
xhr.onreadystatechange = function() {
var DONE = 4; // readyState 4 means the request is done.
var OK = 200; // status 200 is a successful return.
if (xhr.readyState === DONE) {
if (xhr.status === OK) {
// console.log(xhr.responseText);
console.log(JSON.parse(xhr.responseText));
} else {
console.log('Error: ' + xhr.status);
}
}
};
}
Then I call the function in another step by doing GetGitInfo(); which all works fine.
However, If I wanted to call the function and replace the gitURL variable how would I achieve that?
So something like
GetGitInfo(
gotURL= "https://api.github.com/users/new_user/repo";
);
You can't modify a local variable to a function from outside the function. They are private to the function's implementation.
But, since it's your own function, you can just create an argument that can be passed into the function. You can even make the argument optional so it will take your initial value as the default value if it is not passed.
var GetGitInfo = function(url) {
var xhr = new XMLHttpRequest();
var gitURL = url || "https://api.github.com/users/myself/repos";
xhr.open("GET", gitURL);
xhr.send(null);
xhr.onreadystatechange = function() {
var DONE = 4; // readyState 4 means the request is done.
var OK = 200; // status 200 is a successful return.
if (xhr.readyState === DONE) {
if (xhr.status === OK) {
// console.log(xhr.responseText);
console.log(JSON.parse(xhr.responseText));
} else {
console.log('Error: ' + xhr.status);
}
}
};
}
Then, you can use the function the way you were using it or you can pass in an URL to use:
getGitInfo(); // uses your default URL
getGitInfo("http://someURL"); // uses the URL you pass in
FYI, this function looks like it will ultimately need to either return a promise or accept a callback so you can communicate the results back to the caller.
From the snippet above you need to set the url as a function parameter so when calling it uses the specified url.
var GetInfo = function(url) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.send(null);
xhr.onreadystatechange = function() {
var DONE = 4; // readyState 4 means the request is done.
var OK = 200; // status 200 is a successful return.
if (xhr.readyState === DONE) {
if (xhr.status === OK) {
// console.log(xhr.responseText);
console.log(JSON.parse(xhr.responseText));
} else {
console.log('Error: ' + xhr.status);
}
}
};
GetInfo("https://api.github.com/users/myself/repos");
You should do a toString() on the function:
GetGitInfo.toString()
Then you should do a text search and replace on the variable and it's data:
GetGitInfo.toString().substring(0,GetGitInfo.indexOf('somestring'))+'gitUrl="newURL"'+GetGitInfo.toString().substring(.......)
Then you should eval that string!
Or, you know, use function parameters. Either way. Whatever's easiest.
Pass a parameter to the function:
var GetGitInfo = function(gitURL) {
var xhr = new XMLHttpRequest();
xhr.open("GET", gitURL);
xhr.send(null);
xhr.onreadystatechange = function() {
var DONE = 4; // readyState 4 means the request is done.
var OK = 200; // status 200 is a successful return.
if (xhr.readyState === DONE) {
if (xhr.status === OK) {
// console.log(xhr.responseText);
console.log(JSON.parse(xhr.responseText));
} else {
console.log('Error: ' + xhr.status);
}
}
};
}
GetGetInfo("https://api.github.com/users/myself/repos");
Just add a parameter to your function:
var GetGitInfo = function(gitURL) {
var xhr = new XMLHttpRequest();
xhr.open("GET", gitURL);
xhr.send(null);
xhr.onreadystatechange = function() {
var DONE = 4; // readyState 4 means the request is done.
var OK = 200; // status 200 is a successful return.
if (xhr.readyState === DONE) {
if (xhr.status === OK) {
// console.log(xhr.responseText);
console.log(JSON.parse(xhr.responseText));
} else {
console.log('Error: ' + xhr.status);
}
}
};
}
and call it like this:
GetGitInfo("https://api.github.com/users/myself/repos");
Use the parameters
var getData = function(url){
// url can be used here
}
var data = getData("http://apiurl.xy")
I have this code to make an ajax request, but according to Chrome Inspector the callback associated with the request is being called twice (by this I mean the response is being logged into the console twice), 2 more logs are being printed without any content. Here's the code:
var ajax = {
pull: function (settings) {
settings.type = 'get';
settings.callback = typeof (settings.callback) === 'function' ? settings.callback : false;
settings.data = settings.data ? settings.data : null;
return this.request(settings.url, settings.type, settings.callback, settings.data);
},
request: function (url, type, callback, data) {
var ids = ['MSXML2.XMLHTTP.3.0',
'MSXML2.XMLHTTP',
'Microsoft.XMLHTTP'],
xhr;
if (window.XMLHttpRequest) {
xhr = new XMLHttpRequest();
} else {
for (var i = 0; i < ids.length; i++) {
try {
xhr = new ActiveXObject(ids[i]);
break;
} catch (e) {}
}
}
if (callback) {
xhr.onreadystatechange = function () {
callback(xhr);
};
}
xhr.open(type, url, true);
if (type.toUpperCase() === 'GET') {
xhr.send();
} else if (type.toUpperCase() === 'POST') {
xhr.send(data);
}
}
}
ajax.pull({
url: 'http://localhost/my/twtools/scripts/ajax.php',
callback: function (xhr) {
console.log(xhr.response);
}
});
xhr.onreadystatechange has several steps (numbered from 0 top 4 I do believe something like 0 = uninitialized, 1 = starting etc, although I can't rember the exact names of the steps anymore, a quick google should find them), and each step is calling your callback. If I remember correctly, the last stage is 4, so I do believe you need to check something like this
if (xhr.readyState == 4 && xhr.status == 200)
{
// call has finished successfully
}
inside you callback, i.e. to check that it is all finished and got a successful response
I've been spoilt by jQuery these days (so much easier to do with jQuery), been quite a while since I wrote raw ajax
You're using onreadystatechange, which gets called more than once (once each state change).
Try using
xhr.onload = function() {
callback(xhr);
};
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.");
}
}
};
}
I'm looking to setup a web page that samples data via AJAX calls from an embedded web-server. How would I set up the code so that one request doesn't overlap another?
I should mention I have very little JavaScript experience and also a compelling reason not to use external libraries of any size bigger than maybe 10 or so kilobytes.
You may want to consider the option of relaunching your AJAX request ONLY after a successful response from the previous AJAX call.
function autoUpdate()
{
var ajaxConnection = new Ext.data.Connection();
ajaxConnection.request(
{
method: 'GET',
url: '/web-service/',
success: function(response)
{
// Add your logic here for a successful AJAX response.
// ...
// ...
// Relaunch the autoUpdate() function in 5 seconds.
setTimeout(autoUpdate, 5000);
}
}
}
This example uses ExtJS, but you could very easily use just XMLHttpRequest.
NOTE: If you must have an exact interval of x seconds, you would have to keep track of the time passed from when the AJAX request was launched up to the setTimeout() call, and then subtract this timespan from the delay. Otherwise, the interval time in the above example will vary with the network latency and with the time to processes the web service logic.
I suggest you use a small toolkit like jx.js (source). You can find it here: http://www.openjs.com/scripts/jx/ (less than 1k minified)
To setup a request:
jx.load('somepage.php', function(data){
alert(data); // Do what you want with the 'data' variable.
});
To set it up on an interval you can use setInterval and a variable to store whether or not a request is currently occuring - if it is, we simple do nothing:
var activeRequest = false;
setInterval(function(){
if (!activeRequest) {
// Only runs if no request is currently occuring:
jx.load('somepage.php', function(data){
activeRequest = false;
alert(data); // Do what you want with the 'data' variable.
});
}
activeRequest = true;
}, 5000); // Every five seconds
AJAX, despite the name, need not be asynchronous.
Here is the asynchronous method...
var req;
function ajax(method,url,payload,action)
{
if (window.XMLHttpRequest)
{
req = new XMLHttpRequest();
req.onreadystatechange = action;
req.open(method, url, true);
req.send(payload);
}
else if (window.ActiveXObject)
{
req = new ActiveXObject("Microsoft.XMLHTTP");
if (req)
{
req.onreadystatechange = action;
req.open(method, url, true);
req.send(payload);
}
else
{
alert("Could not create ActiveXObject(Microsoft.XMLHTTP)");
}
}
}
...but here is a synchronous equivalent...
function sjax(method,url,payload,action)
{
if (window.XMLHttpRequest)
{
req = new XMLHttpRequest();
req.open(method, url, false);
req.send(payload);
action();
}
else if (window.ActiveXObject)
{
req = new ActiveXObject("Microsoft.XMLHTTP");
if (req)
{
req.onreadystatechange = action;
req.open(method, url, false);
req.send(payload);
}
else
{
alert("Could not create ActiveXObject(Microsoft.XMLHTTP)");
}
}
}
... and here is a typical action ...
function insertHtml(target)
{
var pageTarget = arguments[0];
if (req.readyState == 4) // 4 == "loaded"
{
if (req.status == 200) // 200 == "Ok"
{
if (req.responseText.indexOf("error") >= 0)
{
alert("Please report the following error...");
pretty = req.responseText.substring(req.responseText.indexOf("error"),1200);
pretty = pretty.substring(0,pretty.indexOf("\""));
alert(pretty + "\n\n" + req.responseText.substring(0,1200));
}
else
{
div = document.getElementById(pageTarget);
div.innerHTML = req.responseText;
dimOff();
}
}
else
{
alert("Could not retreive URL:\n" + req.statusText);
}
}
}