Asynchronous Ajax Call Mixing Up Callbacks - javascript

So basically I have an ajax function pretty standard one. Like so:
function ajax_call(rest_req, url, success_callback, fail_callback) {
// if (request_in_progress)
// return;
// request_in_progress = true;
var xhttp = new XMLHttpRequest;
xhttp.onreadystatechange = function () {
if (this.readyState == 4) {
// request_in_progress = false;
if (this.status == 200) {
success_callback(this);
}
else {
fail_callback(this);
}
}
};
xhttp.open(rest_req, url, true);
xhttp.send();
}
When I use the ajax function this way:
(function() {
function setup() {
ajax_call("GET", "url1", function(xhttp) {
response = JSON.parse(xhttp.responseText);
if (response["error"] != 100)
document.getElementById('url1-reading').innerHTML = '---';
else
document.getElementById('url1-reading').innerText = response["response"];
},
function() {}
);
ajax_call("GET", "url2" , function(xhttp) {
response = JSON.parse(xhttp.responseText);
if (response["error"] != 100)
document.getElementById('url2-reading').innerHTML = '---';
else
document.getElementById('url2-reading').innerText = response["response"];
},
function() {}
);
console.log('Refresh');
}
setInterval(setup, 1000);
})();
This code behaves differently than what I expect. When I run this code, there are some times when the results that were suppose to go to url1 success_callback goes inside url2's success_callback.
To put another way the response variable inside url1 ajax_call is what I expected to show up as response variable for url2. So in effect the ajax_call seem to not know what success_callback is for what even though I explicitly pass it in as a parameter.
I'm coming from a C++ background so this is a difficult concept to grasp. How do I do this the right way? I hope my question is clear. Please tell me what is not clear so I can clarify.

The way you declare it, response is a global variable. Try changing response = to let response =

Related

Understanding XHR request object in javascript... (confused)

I'm following a simple book and It says:
function createRequest()
{
try
{
request = new XMLHttpRequest();
}
catch (tryMS)
{
try
{
request = new ActiveXObject("Msxml2.XMLHTTP");
}
catch (otherMS)
{
try
{
request = new ActiveXObject("Microsoft.XMLHTTP");
}
catch (failed)
{
request = null;
}
}
}
return request;
}
function getDetails(itemName)
{
var request = createRequest();
if (request==null)
{ alert("Unable to create request");
return;
}
var url= "getDetails.php?ImageID=" + escape(itemName);
request.open("GET",url,true);
request.onreadystatechange = displayDetails;
request.send(null);
}
function displayDetails()
{
if (request.readyState == 4)
{
if (request.status == 200)
{
detailDiv = document.getElementById("description");
detailDiv.innerHTML = request.responseText;
}
}
}
And all this code above is fine and it's okay to me.. but after few pages it says:
ITS VERY IMPORTANT TO REMOVE VAR KEYWORD BEFORE request VARIABLE so the callback can reference the variable...
but how come in example above it worked? is it coincidence if we call a variable 'request' that it will map with global variable in a createRequest method?
Take a look on image below:
Why is this happening ? in one example var before request variable is used and everything is fine, in another var is avoided so the method in callback might access it.. but how come method in a callback is accessing a request variable in first example...
It's confusing because there are 2 similar examples, with different explanations..
EDIT
P.S it says request has to be a global ? :o
Thanks guys
Cheers
In both examples, implicit global variables are created so they can be shared with the callback.
When the second request variable is created, it creates a local variable inside the getDetails function. So when createRequest() returns the global variable, the local variable becomes a reference to it.
This is rather bad advice and shows a lack of understanding on the writers' part. But it seems to be an old text, since activeX objects are deprecated by now, so maybe globals used to be less frowned upon. The proper way is to either send the responseText or responseXML as a parameter to the callback or send the entire request as the parameter for the callback.
Maybe the writer didn't want to make the request code more complex, but imho, this is not a good way to teach people things.
function createRequest( method, url, callback, payload ) {
var request = new XMLHttpRequest();
if ( !request ) {
alert( "Unable to create request" );
return null;
}
request.open( method, url );
request.onreadystatechange = function() {
if (request.readyState === 4 && request.status === 200 ) {
callback( request.responseText );
}
};
request.send( payload );
};
function getDetails( itemName, callback ) {
createRequest( "GET", "getDetails.php?ImageID=" + escape(itemName), callback, null );
};
function displayDetails( detail ) {
var detailDiv = document.getElementById("description");
detailDiv.innerHTML = detail;
};
getDetails( "someItemName", displayDetails );
you are right, in your first example, function createRequest is not using var, which mean you are creating a global variable request when excute request = new XMLHttpRequest();.
We should avoid using gobal var in most situation.
function createRequest() {
try {
// add var so it's not global variable
var request = new XMLHttpRequest();
} catch (tryMS) {
try {
request = new ActiveXObject("Msxml2.XMLHTTP");
} catch (otherMS) {
try {
request = new ActiveXObject("Microsoft.XMLHTTP");
} catch (failed) {
request = null;
}
}
}
return request;
}
function getDetails(itemName)
{
var request = createRequest();
if (request==null)
{ alert("Unable to create request");
return;
}
var url= "getDetails.php?ImageID=" + escape(itemName);
request.open("GET",url,true);
// create anonymous function to call your callback and pass `request` as local variable
request.onreadystatechange = function(){
displayDetails(request);
};
request.send(null);
}
function displayDetails(request)
{
if (request.readyState == 4)
{
if (request.status == 200)
{
detailDiv = document.getElementById("description");
detailDiv.innerHTML = request.responseText;
}
}
}

JavaScript , setTimeout

I have to create an input type text form on my webpage to modify a setTimeout value based on user input in my javascript code while its running. Can i do that and if i can how can i?
I tired to send a String to my backend from user input and than with ajax calls assign the String to a variable in javascript but it does not work because of the inner html is a String , and when i converted it to an int it does not work also.
function getData() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("idovalt").innerHTML =
this.responseText;
}
};
xhttp.open("GET", "toltes", true);
xhttp.send();
}
Here is an input form:
var idovalt = 1000;
setTimeout(function(){ize()}, idovalt);
Strring or integer is irrelevant to the matter.
HTML:
<input type="text" id="idovalt" value="1000">
JS:
function ize() {
// do something
}
setTimeout(ize, document.getElementById('idovalt').value);
If you want to write some code directly to the first argument of setTimeout(), the code should be quoted.
e.g.,
setTimeout('alert("Hello!")', 1000);
Yes can approach this, i going to use Jquery for event handling, but of course may change it.
var myInput = $("#timeoutDelay"),
timeOut = null;
function setTimeOutOnInputChange(timeout) {
timeOut = setTimeout(function () {
console.log("Your code...");
//Ize();
}, timeout);
}
// I did not get why you are doing this, asynchronous call execute after the timeout function, thats why you need to wait until is ready to change de timeout, in case time out value comes from server and not for user input, otherwise it doesn't makes sense.
function getData() {
var xhttp = new XMLHttpRequest();
xhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
document.getElementById("idovalt").innerHTML = this.responseText;
setTimeOutOnInputChange(parseInt(this.responseText)); // Assuming does not return an object
}
};
xhttp.open("GET", "toltes", true);
xhttp.send();
}
myInput.change(function () {
var val = this.value;
if (timeOut != null) {
clearTimeout(timeOut);
}
if (isNaN(val)) {
console.error("Input is not a number");
} else {
setTimeOutOnInputChange(val);
}
});

return value = undefined when i try to call a Ajax-function inside another Ajax-function

i have a function with a XML-HTTP-Request. Unfortunately i don't get back my DB-result when i call this function getUserdataByToken() <-- working, via a second Function sendPost(wall).
I just want to have the return value (array) inside my second function but the value is always "undefined". Can someone help me?
function getUserdataByToken() {
var token = localStorage.getItem("token");
var userDataRequest;
//-AJAX-REQUEST
var xhttp;
if (window.XMLHttpRequest) {
xhttp = new XMLHttpRequest();
} else {
// code for IE6, IE5
xhttp = new ActiveXObject("Microsoft.XMLHTTP");
}
var url= window.location.protocol+"//"+window.location.host+"/getuserdatabytoken";
var param = "token=" + token;
xhttp.onreadystatechange = function() {
if (xhttp.readyState == 4 && xhttp.status == 200) {
userDataRequest = JSON.parse(xhttp.responseText);
if (userDataRequest.success === "false") {
warningMessage('homeMessage', false, userDataRequest.message);
} else {
return userDataRequest;
}
}
};
xhttp.open("POST", url, true);
xhttp.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
xhttp.send(param);
}
Function call via second Function (AJAX) leads too "undefined" Value for "userDataRequest" (return of function 1).
function sendPost(wall) {
var content;
var token = localStorage.getItem("token");
var userData = getUserdataByToken(); // PROBLEM
console.log(userData); // "leads to undefined"
alert(userData); // "leads to undefined"
… Ajax Call etc…
P.S. it's my first post here in stackoverflow, I'm always grateful for tips.
Thanks!
The userdata value only exists within the anonymous Ajax callback function, and you only return it from there. That is pointless because there is nowhere for it to be returned to; certainly the value does not get returned from getUserdataByToken. Don't forget that Ajax calls are asynchronous; when sendPost calls getUserdataByToken the request won't even have been made.
Generally you'd be much better off using a library like jQuery for this whole thing. Apart from making your code much simpler, it will allow you to use things like Promises which are explicitly intended to solve this kind of problem.
(And, do you really need to support IE5? Are you sure?)

JSON parse error: Cannot read property

I have created some little jt code, but it gives me error
function Mind(){
var request = "request";
var reply = "reply";
var words = '';
this.Reply = function(){
if(request == words.nouns[0].noun){
reply = words.nouns[0].noun;
}
else
reply = this.words.nouns[0].noun;
}
this.SetRequest = function(req){
request = req;
}
this.GetReply = function(){
return reply;
}
this.Parse = function(u){
var xmlhttp = new XMLHttpRequest();
var url = u;
var result;
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
words = JSON.parse(xmlhttp.responseText);
}
}
xmlhttp.open("GET", url, true);
xmlhttp.send();
return result;
}
this.Construct = function(){
words = this.Parse('mind/words.json');
}}
var mind = new Mind();
mind.Parse('mind/words.json');
and here is my json file
{
"nouns": [
{"noun": "child"},
{"noun": "father"}
]
}
In command live all goes well, but when I run this code, appears error
Uncaught TypeError: Cannot read property 'nouns' of undefined
Mutliple errors. The most fundamental one is that your code ignores that XMLHttpRequest is async, and wont return a value in the same way as "regular" functions. Read about it here: How to make a function wait until a callback has been called using node.js. The TL;DR is that you have to pass in a "callback-function" to your parse-method and "return" your value using that function, instead of using a return-statement. Google for "javascript callbacks" and read a few tutorials if this concept is new to you!
You also have some minor errors, like returning result from Parse, but never actually setting result to anything. Also words is being assigned in multiple places in a way that doesn't really make sense. But both of these things will go away when you solve the sync/async issues.
EDIT:
Essentially the fix looks like this:
this.Parse = function(u, callback){ // this "callback" is new
var xmlhttp = new XMLHttpRequest();
var url = u;
var result;
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState == 4 && xmlhttp.status == 200) {
words = JSON.parse(xmlhttp.responseText);
callback(null, words); // we're "returning" the words here
}
}
xmlhttp.open("GET", url, true);
xmlhttp.send();
// no return statement here!
}
this.Construct = function(){
this.Parse('mind/words.json', function(error, words) {
// here you can use your words!
});
}}

Callback of function stocked in an object

I need to do a sequential XMLHttpRequest requests (FIFO) to not to call the server with many requests a same time, I wrote this function that do the XMLHttpRequest sequentially:
var queue = [];
var xmlHttpCurrentlyOccuped = false;
function loadUserDetails() {
var url = "https://someurl.com";
doWebRequest("GET", url, "", parseUserDetails);
}
function parseUserDetails(dataFromServer){
Console.log("data received from server: "+JSON.stringify(dataFromServer));
}
function doWebRequest(method, url, params, callback) {
var parametres = new Object();
parametres.myMethod = method;
parametres.myUrl = url;
parametres.myParams = params;
parametres.myCallback = callback;
queue.push(parametres);
while (queue.length>0 && !xmlHttpCurrentlyOccuped){
var doc = new XMLHttpRequest();
doc.onreadystatechange = function() {
var status;
if (doc.readyState == XMLHttpRequest.LOADING || doc.readyState == XMLHttpRequest.OPENED){
xmlHttpCurrentlyOccuped = true;
}
if (doc.readyState == XMLHttpRequest.DONE && doc.status == 200) {
xmlHttpCurrentlyOccuped = false;
var data;
var contentType = doc.getResponseHeader("Content-Type");
data = doc.responseText;
queue[0].myCallback(data);
queue.shift();
}
else if (doc.readyState == XMLHttpRequest.DONE) {
xmlHttpCurrentlyOccuped = false;
status = doc.status;
if(status!=200) {
parseTheError(url);
}
queue.shift();
}
}
doc.open(queue[0].myMethod, queue[0].myUrl);
doc.send();
}
}
My problem is, after the XMLHttpRequest is done well, the callback function is not working in this line of my code queue[0].myCallback(data);I have this error: "queue[0].callback(data): undefined".
Any idea to resolve this issue?
Update:
I resolved the issue, this is my working code maybe it can help someone:
var queue = [];
function doWebRequest(method, url, params, callback) {
var parametres = new Object();
parametres.myMethod = method;
parametres.myUrl = url;
parametres.myParams = params;
parametres.myCallback = callback;
if (queue.length>0) {if (queue[queue.length-1].url != parametres.url) queue.push(parametres);}
else queue.push(parametres);
var doc = new XMLHttpRequest();
function processNextInQueue() {
if (queue.length>0){
var current = queue.shift();
doc.onreadystatechange = function() {
if (doc.readyState == XMLHttpRequest.DONE){
if(doc.status == 200) {
if(typeof current.myCallback == 'function'){
current.myCallback(doc.responseText)
} else {
console.log('Passed callback is not a function');
}
processNextInQueue();
}
else if(doc.status!=200) {
parseTheErrors(current.myUrl);
}
}
}
doc.open(current.myMethod, current.myUrl);
doc.send();
}
}
processNextInQueue();
}
Thank you guys for your help ;)
You can't poll in javascript with a while loop like this and expect proper performance. Javascript is single threaded so when you poll like this, you don't allow any cycles for other things to happen. You need to learn how to write asynchronous code where you start the first ajax call and then return. When that first one completes, you then start the second one and so on.
Here's a way to do this:
queue.push(parametres);
function processNextInQueue() {
if (queue.length) {
var doc = new XMLHttpRequest();
doc.onreadystatechange = function() {
if (doc.readyState == XMLHttpRequest.DONE) {
if (doc.status == 200) {
queue[0].myCallback(doc.responseText);
} else {
fonctionPourAnalyserLErreur(url);
}
// done now so remove this one from the queue
// and start the next one
queue.shift();
processNextInQueue();
}
}
doc.open(queue[0].myMethod, queue[0].myUrl);
doc.send();
}
}
processNextInQueue();
The idea is that you fire off the first ajax call and then you just return. When the readystatechange shows it is done, you process the results and then fire off the next one. All the while the ajax call is in process, the javascript engine is free to service other events and do other things (that's the key to handling an asynchronous operation like an ajax call).
In this line: queue[0].myCallback(data), for debugging purposes (and to prevent errors from breaking your site) I would change to the following:
var current = queue.shift();
if(typeof current.myCallback == 'function'){
current.myCallback(data)
} else {
// for now, log it
console.log('Passed callback is not a function');
}
Also, have you tried just passing an anonymous function to make sure it's not a scope/function hoisting issue?
function loadUserDetails() {
var url = "https://someurl.com";
doWebRequest("GET", url, "", function(dataFromServer){
console.log("data received from server: "+JSON.stringify(dataFromServer));
});
}

Categories