Javascript, variable reference - javascript

In a situation like the code below how would you go about accessing a variable value that is in an anonymous function? I would like to return the bool value of filterData(xmlhttp.responseText, thisForm); which will be boolean to the main checkAvailable function. Thanks in advance.
function checkAvailable(thisForm) {
var xmlhttp = httpRequest();
var isValid = true;
var un = document.getElementById('u_username').value;
var email = document.getElementById('u_email').value;
xmlhttp.onreadystatechange = function(isValid) {
if (xmlhttp.readyState == 4) {
//I WANT TO ACCESS THIS isValid VARIABLE FROM MAIN FUNCTION checkAvailable
isValid = filterData(xmlhttp.responseText, thisForm);
}
}
xmlhttp.open("GET","profile_fetch_reg_info.php?do=available&un="+un+"&email="+email+"",true);
xmlhttp.send(null);
return isValid;
}
The way i have it now
function validateRegForm(thisForm) {
var isValid = true;
var warningIcon = "";//for later in case we want to use an icon next to warning msg
checkAvailable(thisForm, function(isValid) { });
if(isValid == false)
window.scroll(0,0);
alert(isValid);
return false;//isValidForm;
}
function checkAvailable(thisForm, resultFunction) {
var xmlhttp = httpRequest();
var un = document.getElementById('u_username').value;
var email = document.getElementById('u_email').value;
xmlhttp.onreadystatechange = function(isValid) {
if(xmlhttp.readyState == 4) {
isValid = filterData(xmlhttp.responseText, thisForm);
resultFunction(isValid);
}
}
xmlhttp.open("GET","profile_fetch_reg_info.php?do=available&un="+un+"&email="+email+"",true);
xmlhttp.send(null);
}

Modify the checkAvailable function to take an additional parameter which is the function to call with the result.
function checkAvailable(thisForm, resultFunction) {
..
xmlhttp.onreadystatechange = function(isValid) {
if (xmlhttp.readyState == 4) {
//I WANT TO ACCESS THIS isValid VARIABLE FROM MAIN FUNCTION checkAvailable
isValid = filterData(xmlhttp.responseText, thisForm);
resultFunction(isValid);
}
}
}
Then, you can call it something like this:
checkAvailable(thisForm, function(isValid) {
// Use the isValid value which is the result of the checkAvailable call.
});
EDIT
Here is a change to the modified code you posted.
function validateRegForm(thisForm) {
var isValid = true;
var warningIcon = "";//for later in case we want to use an icon next to warning msg
checkAvailable(thisForm, function(isValid) {
if(isValid == false)
window.scroll(0,0);
alert(isValid);
}
// WARNING!! This will happen before the result is discovered.
// You'll need to modify the function that called into validateRegForm.
// It should not wait for a return parameter either.
return false;//isValidForm;
}

You need to make your XmlHttpRequest synchronous, you can do this by setting the last parameter of .open() to false, i.e.
xmlhttp.open("GET","profile_fetch_reg_info.php?do=available&un="+un+"&email="+email+"",false);
However this will lock your UI/SJS for the duration of the call

In general terms, the way to return a value from an anonymous function would be to make use of the lexical scoping nature of Javascript. To do this, you would need to declare a variable in the same scope as the anonymous function and have the function set the variable during its execution.
For example:
function a() {
var x = 1;
(function() { x = 2; })();
alert(x); // x will be 2
}
However, this is all predicated on the fact that the execution is linear, meaning that the the alert happens after the anonymous function is executed. In the code you presented above, this wouldn't happen because the XMLHttpRequest is asynchronous meaning that the onreadystatechange callback will be called at some other point in time. You could change your XMLHttpRequest to be synchronous but this would lock up the UI of your page while the request is on progress.
function checkAvailable(thisForm) {
var xmlhttp = httpRequest();
var isValid = true;
var un = document.getElementById('u_username').value;
var email = document.getElementById('u_email').value;
xmlhttp.open("GET","profile_fetch_reg_info.php?do=available&un="+un+"&email="+email+"",false);
xmlhttp.send(null);
isValid = filterData(xmlhttp.responseText, thisForm);
return isValid;
}
The best way to work with this sort of situation is to move to a completely asynchronous model. In this model, your checkAvailble() function would be restructured so that it takes a callback that is invoked after the validity is determined. Below is an example of what this might look like:
function whenAvailable(theForm, callback) {
var xmlhttp = httpRequest();
var un = document.getElementById('u_username').value;
var email = document.getElementById('u_email').value;
xmlhttp.onreadystatechange = function() {
if (xmlhttp.readyState === 4) {
if (callback) {
var isValid = filterData(xmlhttp.responseText, thisForm);
callback.call(null, isValid);
}
}
}
xmlhttp.open("GET","profile_fetch_reg_info.php?do=available&un="+un+"&email="+email+"",true);
xmlhttp.send(null);
}
A call to this function would look like the following:
whenAvailable(document.getElementById('someForm'), function(valid) {
if (valid) {
// do something when valid
} else {
// do soemthing when invalid
}
});

If you need to wait for the response to proceed, use SJAX (synchronous javascript and xml) to get the result. Your script will not continue until you have received your response. Beware that timeout errors are not handled well in ie.
On the otherhand, you can use AJAX and do what you need to do inside the readyState check, instead of trying to return the value for something else to handle it.

You need to pass false as the third parameter to xmlhttp.open(). That will cause the request to be executed synchronously and your program execution will pause until it completes.
If you do this, you won’t need the anonymous function. You can just get xmlhttp.responseText directly:
function checkAvailable(thisForm) {
var xmlhttp = httpRequest();
var isValid = true;
var un = document.getElementById('u_username').value;
var email = document.getElementById('u_email').value;
xmlhttp.open("GET", your_parameters, false);
xmlhttp.send(null);
isValid = filterData(xmlhttp.responseText, thisForm);
return isValid;
}
The main drawback to this is that the browser will basically freeze until the xmlhttp.send() call completes.
If you can refactor your code so that the synchronous call is not needed, that would be better IMO.

Create an object outside the function...
obj = new Object;
obj.isValid = false;
obj.complete = false;
Then your code... except
obj.isValid = filterData(xmlhttp.responseText, thisForm);
objt.complete = true;
Make sure object definition is above the function, so that it appears global.
Now on the otherside you can detect if state 4 was achieved, and if it has then you can grab the object value.
You might also need to pass the object through in your function decalarion, it should pass it by ref and update.

The way you're accessing it within the scoped function is fine, the problem is what you're trying to do doesn't work the way you're expecting it to. As others have mentioned, you could go with a synchronous request, however that's probably not what you really want (since it'll hang your UI).
Looking at your code, I assume you have a function that needs to know whether or not your request was successful, and then continue processing. So, instead of using a flag, you need to have a callback that can execute one completion of the ajax request. Something like the following:
function checkAvailable(thisForm, callback) {
var xmlhttp = httpRequest();
var isValid = true;
var un = document.getElementById('u_username').value;
var email = document.getElementById('u_email').value;
xmlhttp.onreadystatechange = function(isValid) {
if (xmlhttp.readyState == 4) {
//I WANT TO ACCESS THIS isValid VARIABLE FROM MAIN FUNCTION checkAvailable
isValid = filterData(xmlhttp.responseText, thisForm);
}
callback(isValid);
}
xmlhttp.open("GET","profile_fetch_reg_info.php?do=available&un="+un+"&email="+email+"",true);
xmlhttp.send(null);
}

Cheat: make the ajax request handler update data in a hidden element. Then, your polling (I presume) query function can look at the hidden element to see if the "ready" flag was finally set.

Related

Javascript Passing Variable on Click

Im learning JS and working with passing variables between functions. I am trying to execute a function on click whilst passing a variable between functions. I can pass the variable by executing the function on page load, but cant do it on a click function.
function getData() {
let xmlhttp = new XMLHttpRequest();
let url = "https://jsonplaceholder.typicode.com/posts";
xmlhttp.onreadystatechange = function() {
if (this.readyState == 4 && this.status == 200) {
myArr = JSON.parse(this.responseText);
//myFunction(myArr);
document.getElementById("start2").addEventListener("click", myFunction(myArr));
}
};
xmlhttp.open("GET", url, true);
xmlhttp.send();
}
getData();
function myFunction(arr) {
// var arr = getData();
var out = "";
var i;
for (i = 0; i < arr.length; i++) {
out += '<p>' + arr[i].id + '</p><br>';
}
document.getElementById("answer-holder").innerHTML = out;
}
It does not work, since addEventListener expects a function as its second argument. But you do not provide a function. Instead you provide the result of evaluating myFunction, which is undefined (since myFunction does not return anything).
// when `addEventListener` is called, `myFunction` is also called immediately,
// instead of first being called when the event is triggered
document.getElementById("start2").addEventListener("click", myFunction(myArr));
You can fix the code by instead providing a function, that calls myFunction with your chosen argument.
document.getElementById("start2").addEventListener("click", function() {
myFunction(myArr);
});
The problem is you are calling the function when trying to set the onClick listener:
document.getElementById("start2").addEventListener("click", myFunction(myArr));
As suggested by Thomas, you have to pass a function to addEventListener. However, I would suggest the newer lambda notation:
document.getElementById("start2").addEventListener("click", () => myFunction(myArr));

how to get a value out of a function? [duplicate]

This question already has answers here:
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 5 years ago.
I am trying to get value out of java script function to a variable
var isMember;
IsCurrentUserMemberOfGroup("Team Management System Members", function (isCurrentUserInGroup) {
if (isCurrentUserInGroup) {
//alert("Admin");
isMember = true;
return true;
}
else {
isMember = false;
//alert("NoAdmin")
}
});
alert(isMember);
the other function used to be called
function IsCurrentUserMemberOfGroup(groupName, OnComplete) {
var currentContext = new SP.ClientContext.get_current();
var currentWeb = currentContext.get_web();
var currentUser = currentContext.get_web().get_currentUser();
currentContext.load(currentUser);
var allGroups = currentWeb.get_siteGroups();
currentContext.load(allGroups);
var group = allGroups.getByName(groupName);
currentContext.load(group);
var groupUsers = group.get_users();
currentContext.load(groupUsers);
currentContext.executeQueryAsync(OnSuccess, OnFailure);
function OnSuccess(sender, args) {
var userInGroup = false;
var groupUserEnumerator = groupUsers.getEnumerator();
while (groupUserEnumerator.moveNext()) {
var groupUser = groupUserEnumerator.get_current();
if (groupUser.get_id() == currentUser.get_id()) {
userInGroup = true;
break;
}
}
OnComplete(userInGroup);
}
function OnFailure(sender, args) {
OnComplete(false);
}
}
but when I execute i am getting the value : undefined
thanks
You have to ensure that the function associated with IsCurrentUserMemberOfGroup gets invoked before you attempt to check the value of isMember. You can't just put an alert() after the function's code and expect that it will run the function first.
Here's a working example:
var isMember;
function checkUser(isCurrentUserInGroup) {
if (isCurrentUserInGroup) {
isMember = true;
} else {
isMember = false;
}
}
alert(isMember); // undefined because function hasn't bee called yet
checkUser(); // invoke the function with no data being passed in
alert(isMember); // false because undefined was passed into function
checkUser("Something"); // invoke the function with data being passed in
alert(isMember); // true because something was passed into function
most likely there's some asynchronous stuff going inside IsCurrentUserMemberOfGroup, the callback you're passing will be postponed as soon as your async stuff completes.
Potentially your IsCurrentUserMemberOfGroup function could look like this this:
function IsCurrentUserMemberOfGroup(someString, callback){
..
//async stuff here
myAPI.asyncMethod(callback);
//here the parser will go on with the synchronous code and park in the event queue the callback for the async method
..
}
as soon as the parser encounter the async function, the callback will be parked inside the event queue. the async function, (e.g. reading from file, deal with the network, etc.) will be handled by some other OS task that will notify the javascript thread when they finish, so that the callback could be finally invoked.
The problem is here.
the parser executes synchronously, line by line, so it must keep going to not block the ui, that's why it reach the alert function before the async stuff completes, and print undefined, because member is still not assigned.

callback() produces a snytax error. Object not a function

var ajax = function(url,callback) {
if(window.XMLHttpRequest) {
xml = new XMLHttpRequest();
}
if(window.ActiveXObject) {
var xml = new ActiveXObject("Microsoft.XMLHTTP");
}
this.xml = xml;
alert(xml);
xml.onreadystatechange = function(callback) {
callback = callback();
if(xml.readyState == 4 && xml.status == 200) {
alert(xml);
}
}
xml.open('GET',url,true);
xml.send(null);
}
ajax('/server/eChck.php?email=email#yahoo.com',function(){
alert(xml);
});
the callback() wont work. Produces syntax error. can any explain to me how I would code this so I could put my callback() inside of the parameter?
Thanks,
Jon W
There are two mistakes here:
xml.onreadystatechange = function(callback) {
callback = callback();
if(xml.readyState == 4 && xml.status == 200) {
alert(xml);
}
}
First, you're creating the ready state change handler as a function that takes a parameter, which isn't incorrect but you've named that parameter "callback". That means that inside the state change handler, "callback" refers to that parameter, not to the "callback" passed in to the outer function.
Second, by assigning to "callback" the result of calling the callback function, you'll overwrite the value each time the event handler is called.
I think you want something like this:
xml.onreadystatechange = function() {
if(xml.readyState == 4 && xml.status == 200) {
callback();
}
}
edit — In addition to those changes, you should declare the "xml" variable in your "ajax" function:
var ajax = function(url,callback) {
var xml;
And take out this line:
this.xml = xml; // take this out

Return Boolean From One Function Must Affect Another

I hope the title of this question is somewhat clear, I couldn't figure out how to exactly spell out my problem. Basically I have 3 functions.
Function A:
function validUsername(){
if(username.element.value.length > 0){
if(checkData(username)){
showMessage(username, true);
}else{
showMessage(username, false, 'Username Already In Use');
}
}
}
Basically this field is using AJAX to verify that a username does not already exist in the database. It uses checkData(username) to get a returned true or false boolean and determine if it needs to show a positive or a negative response.
Function B (checkData(username)):
function checkData(obj){
var field = getFieldValue(obj.container);
if(field.length != 1){
field = null;
}
if(xmlObj != null) {
xmlObj.open("POST", obj.servletUrl, true);
xmlObj.onreadystatechange = function() { handleServerResponse(obj.container); };
xmlObj.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
xmlObj.send("field=" + field); //Post username to Servlet
}
}
This is responsible for creating the AJAX request etc. When the request is ready is uses handleServerResponse to display a message back to the screen.
Function C (handleServerResponse):
function handleServerResponse(container){
var response = xmlObj.responseText;
if(xmlObj.readyState == 4) {
if(xmlObj.status == 200){
if(response.indexOf('null') != -1){
return true;
}else{
return false;
}
}
}
}
Function C returns a boolean value, but how do I also return that boolean value back to Function B so that Function A can receive the response? I have tried:
xmlObj.onreadystatechange = function() { return handleServerResponse(obj.container);
return xmlObj.onreadystatechange; //Function B would return this.
But it doesn't work properly. I have also thought about setting a global variable of something like serverResponse = true and then returning that response from Function B, but other objects also use this function so I would need a way to reset it after each use.
I think your best bet would be to remove function A and place its logic inside Function C:
function handleServerResponse(container){
var response = xmlObj.responseText;
if(xmlObj.readyState == 4) {
if(xmlObj.status == 200){
if(response.indexOf('null') != -1){
showMessage(username, true);
}else{
showMessage(username, false, 'Username Already In Use');
}
}
}
}
If it difficult to return values from AJAX calls because you either have to make them asynchronously, which kind of defeats the purpose, or have to handle the logic you want in the return handler as I've shown above.
The return value of a function executed asynchronously is useless. You need to do the work you're doing in validUsername in the callback or in a function that the callback invokes. As written, you're attempting to use checkData as though it is synchronous, but it fundamentally is not, since it makes an asynchronous HTTP request. Shifting the work into the onreadystatechange callback solves your problem.
The AJAX calls are Asynchronous, therefore the function checkData() return immediatly, without waiting the server answer. When the answer arrives, it calls the handleServerResponse() function passing it the response.
I suggest you to move the validUsername() checks in the handleServerResponse() to be sure you get the correct answer.

Sharing variable JavaScript

In following code, I am taking input
of an AJAX call into a function called
plr(). I want to detect when loading
is complete using the done variable.
But main thread is locking the
variable and the script hangs the
browser. If I put the alert in the
commented place, the purpose is
served. So, what other way can I use
to do the same?
function openX() {
LoadContentInto("Default.aspx", plr);
var obj = null;
done = false;
function plr() {
x = this.AJAXObject.responseText;
t = x.indexOf('{')
n = parseInt(x.substring(0, t));
s = x.substring(t, n + t);
p = eval('(' + s + ')');
obj = p;
done = true;
}
while (done != true)
{ // alert("hello");
}
alert(done);
}
Basically you have to make synchronous your ajax call, so there's no need to create an empty (blocking) while. the callback plr() will be executed on successful response, then remaining data will be called inside that callback
http://www.hunlock.com/blogs/Snippets:_Synchronous_AJAX
You should not wait that actively for the result. When the AJAX call is successfully finished you have the callback function called. In your case it seems that it is plr (although it is not clear what LoadContentInto exactly does).
It seems you have a temptation to make the AJAX success callback synchronous. Sometimes I used to have such passions, but so far it always showed up that there is an asynchronous way as well.
Maybe you want something like that:
function openX() {
LoadContentInto("Default.aspx", plr);
var obj = null;
var done = false; // you have your variable global! Make it local!
function plr() {
x = this.AJAXObject.responseText;
// ...
// put your code here
// ...
alert("Done!");
done = true;
}
setTimeout(function(){
if (!done) {
alert("Please wait!");
// Does the response and/or the operation after the responseText arives take a long time?
// Based on that decide how to inform the user
}
}, 100); // Set the timeout to right value.. based on your needs
}
Few comments to your code:
you have done declared as a global variable, it is very likely that it should be local
while (done != true) is much cleaner as while (!done)

Categories

Resources