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));
Related
Is there a possibility to make some delay? I call a function in a while loop. This function calls executeQueryAsync which has to finish before the loop continues. When I use an alert my code works but without it doesn't.
while (listPermsEnumerator.moveNext()) {
enumG = groups.getEnumerator();
var rAssignment = listPermsEnumerator.get_current();
var member = rAssignment.get_member();
var groupCounter = 1;
var name = '';
//alert(''); This alert makes code work
while (enumG.moveNext()) {
var group = enumG.get_current();
var groupname = group.get_title();
//alert(groupname);
if (member.get_title() === groupname) {
name = groupname;
SP.SOD.executeOrDelayUntilScriptLoaded(function(){
retrieveAllUsersInGroup(groupname, groupCounter, groups);
}, key);
}
groupCounter++;
}
roleAssignment = this.listRoleAssignments.getByPrincipalId(member.get_id());
roleBindings = roleAssignment.get_roleDefinitionBindings();
// in checkPermission() another executeQqueryAsync is called
checkPermission(context, roleAssignment, roleBindings, name);
}
...
function checkPermission(context, roleAssignment, roleBindings, name) {
this.name = name;
context.load(roleAssignment);
context.load(roleBindings);
context.executeQueryAsync(Function.createDelegate(this, Bind), Function.createDelegate(this, BindFail));
}
The simplest solution would be to code your methods in a way that reflects the purpose of asynchronous operations. You seem to be trying to work around the ExecuteQueryAsync and trying to "make" it synchronous.
Here is a similar example -- see the 2nd answer: ( https://sharepoint.stackexchange.com/questions/95907/executequeryasync-in-for-loop ) Basically you a) write the callback function inline, and b) you put the loop in the success callback.
(What's great about writing the "success" callback function in line is the success callback function then has access to all the variables in the method. It's a closure).
If you need to loop through an array of asynchronous jobs, you can do something like this:
var reports = [11, 12, 14, 15];
function doTheReport() {
if (reports.length === 0) {
alert('All reports are done now.');
return;
}
var report_Id = reports.pop();
$.ajax({
url: "/DoTheReport",
complete: function () {
doTheReport();
}
});
};
I was messing around with IndexedDB and I realised that I don't really get event handling in JavaScript.
So here's the code:
var request = indexeddb.open(bla, version);
request.onsuccess = function (event) { };
So the open-method returns a IDBOpenDBRequest object, which, according to Mozillas site, inherits from IDBRequest, which apart from properties and methods also has event handlers, one of them being onsuccess:
https://developer.mozilla.org/en-US/docs/Web/API/IDBRequest.onsuccess
So on the mozilla site, onsuccess is just function () { }
Now, when the database was opened sucessfully, the "onsuccess" event fires and the appropiate event handler is called, in this case the function that I defined. But how exactly does that happen?
The request variable contains an instance of the IDBOpenDBRequest. So when I write request.onsuccess = somefunction(), am I overwriting the default function of the IDBRequest-class?
I dont get why I can write request.onsuccess = somefunction(event) { } and how the event is passed to that function.
EDIT:
function myObect() {
this.open = function(a,b,c) {
if (c > 20) {
this.success("String");
}
};
};
var myrequest = new myObect();
myrequest.open(4,2,21);
myrequest.success = function (ev) {
console.log(ev);
};
To create a similar api, you can do something like:
function open(a, b, c) {
var request = {};
if(c > 20) {
setTimeout(function() {
if(typeof request.success === "function") {
request.success("String");
}
}, 1);
}
return request;
}
var myrequest = open(4, 2, 21);
myrequest.success = function(ev) {
console.log(ev);
};
Here, setTimeout is asynchronous so the callback function is not executed immediately. When any asynchronous task is run in JavaScript, the currently executing code will run to completion before any callback is called. So success is guaranteed to be set before request.success called.
The Indexed DB open call similarly runs an asynchronous task, and then dispatches events when it is finished which will eventually call your callback function.
I overwriting the default function of the IDBRequest-class
Looks like there is no default behavior, so you just set up your own func.
I have the following code in my ajax query:
xhr.onreadystatechange = stateChange;
and the function stateChange is function stateChange(event)
Is it possible to add a second parameter to the function so it doesn't just passes a number as well as the event?
I've tried doing xhr.onreadystatechange = stateChange(event,'123'); with function stateChange(event,num) but it doesn't seem to work.
You can create a closure that allows your event handling function access to those variables.
Instead of
xhr.blahblah;
xhr.onreadystatechange = stateChange;
xhr.blahblah;
This technique creates an anonymous function that gives scope to your '123' variable:
xhr.blahblah;
function (xhrObj, callbackFn, param) {
xhrObj.onreadystatechange = function (event) {
callbackFn(event, param);
};
}(xhr, stateChange, '123');
xhr.blahblah;
Mmmm, sort of:
xhr.onreadystatechange = function(event) { stateChange(event,'123'); };
would do what you say you want, but it isn't clear to me that what you say you want is what you need.
This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 9 years ago.
I have a function which declares a variable with the var keyword. It then launches an AJAX request to set the value of the variable, and this variable is then returned from the function.
However, my implementation fails and I 'don't know why.
Here's a simplified version of the code;
function sendRequest(someargums) {
/* some code */
var the_variable;
/* some code */
request.onreadystatechange =
//here's that other function
function() {
if (request.readyState == 4) {
switch (request.status) {
case 200:
//here the variable should be changed
the_variable = request.responseXML;
/* a lot of code */
//somewhere here the function closes
}
return the_variable;
}
var data = sendRequest(someargums); //and trying to read the data I get the undefined value
AJAX requests are asynchronous. Your sendRuest function is being exectued, the AJAX request is being made but it happens asynchronously; so the remainder of sendRuest is executed, before the AJAX request (and your onreadystatechange handler) is executed, so the_variable is undefined when it is returned.
Effectively, your code works as follows:
function sendRuest(someargums) {
/* some code */
var the_variable;
/* some code */
return the_variable;
}
var data = sendRequest(someargums);
And then some time later, your AJAX request is completing; but it's already too late
You need to use something called a callback:
Where you previously may have had
function () {
var theResult = sendRuest(args);
// do something;
}
You should do:
function () {
sendRuest(args, function (theResult) {
// do something
});
};
and modify sendRuest as follows:
function sendRuest(someargums, callback) {
/* some code */
//here's that other function
request.onreadystatechange =
function() {
if (request.readyState == 4) {
switch (request.status) {
case 200:
callback(request.responseXML);
/* a lot of code */
//somewhere here the function closes
}
}
This is not about scope - its about async processing.
The function sendRuest ends before the onreadystatechange function gets called.
You can't return the variable from the function that creates the ajax callback since the variable won't be set yet.
The ajax function in turn has to call another callback with the returning result.
request.onreadystatechange =
function() {
if (request.readyState == 4) {
switch (request.status) {
case 200:
//here the variable should be changed
the_variable = request.responseXML;
the_callback(the_variable);
Instead a plain string variable, you can use an object.
function sendRuest(someargums) {
var the_variable = {
data: null,
setData: function(data){ this.data = data;}
}
//here's that other function
request.onreadystatechange =
function() {
if (request.readyState == 4) {
switch (request.status) {
case 200:
//here the variable should be changed
the_variable.setData(request.responseXML);
}
return the_variable;
}
Anyway, your last line isn't going to work. When the function 'sendRuest' ends, the XHR request is not completed. You need to use timers to check the value of 'the_variable.data' (very bad) or use callbacks as stated in other answers.
Sergio.
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.