This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 8 years ago.
Is it possible to retrieve variable set in onreadystatechange function from outside the function?
--edit--
Regarding execution of functions:
If its possible i would like to execute ajaxFunction() with one click
and then popup() with next click, or
somehow wait for ajax function to end and then call for alert box
In pseudocode:
function ajaxFunction(){
//creating AJAX
...
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function (){
if(ajaxRequest.readyState == 4){
//success code
======>Here i want to set variable <=====
var MyVariable='MyContent';
}
}
//Retrieving page
....
}
function popup(){
ajaxFunction();
alert(MyVariable);
}
The following code assumes that the ajax-request is synchronous:
function popup(){
ajaxFunction();
alert(MyVariable);
}
But since synchronous requests are blocking the browser you should in almost all cases use asynchronous calls (If I remember correctly onreadystatechange should not be called on synchronous request but different browsers behaves differently)
What you could do is:
function ajaxFunction(callback){
//creating AJAX
...
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function (){
if(ajaxRequest.readyState == 4){
//success code
callback('MyContent')
}
}
//Retrieving page
....
}
function popup() {
ajaxFunction(function(MyVariable){alert(MyVariable););
}
some's comments are correct... this has nothing to do with variable scoping, and everything to do with the fact that the inner function (the 'onreadystatechange' function) setting the value of MyVariable will not have been executed by the time that the alert() happens... so the alert will always be empty.
The inner function doesn't get executed synchronously (ie, right away), but is deferred and executed later, when the request returns, which is long after the alert() has been executed. The only solution to this is to defer the alert until after the request finishes.
But regardless of all this, the inner function can set variables outside its scope, as the other posts mention. But your problem is more about execution order than it is about variable scope.
Why not change MyVariable's scope to outside of the function?
var MyVariable;
function ajaxFunction(){
//creating AJAX
...
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function (){
if(ajaxRequest.readyState == 4){
//success code
======>Here i want to set variable <=====
MyVariable='MyContent';
}
}
//Retrieving page
....
}
function popup(){
ajaxFunction();
alert(MyVariable);
}
No, because MyVariable is scoped to the function. To make it visible to popup(), it must be scoped where both the event handler function and popup() can see it, like Jenp recommends.
Pass the variable to the popup() function:
var MyVariable;
function ajaxFunction(){
//creating AJAX
...
// Create a function that will receive data sent from the server
ajaxRequest.onreadystatechange = function (){
if(ajaxRequest.readyState == 4){
//success code
======>Here i want to set variable <=====
MyVariable='MyContent';
popup(MyVariable);
}
}
//Retrieving page
....
}
function popup(x){
ajaxFunction();
alert(x);
}
This may complicate your problem so feel free to interject, but using a third party Javascript library may help ease your burden slightly (as the commenter some mentioned). Prototype and JQuery both have ways of dealing with scope, such as the bind function in Prototype. Also, you won't have to write your own Ajax request code although I commend you for digging in and seeing how it works! Prototype classes can help with scope issues like you've described. You may want to refactor in order to use asynchronous Ajax requests though so that the browser doesn't have to wait as is the case with how you've written it. The following will popup your alert message and set the variable appropriately, although I haven't tested it for errors. It shows the basic concept though.
var TestClass = Class.create();
TestClass.prototype = {
MyVariable: null,
AjaxURL: "http://yourajaxurl.com/something.asmx",
DoAjaxCall: function() {
new Ajax.Request(this.AjaxURL,
method: 'get',
onSuccess: this.AjaxCallback.bind(this),
onFailure: this.DoSomethingSmart.bind(this));
},
AjaxCallback: function(returnVal) {
this.MyVariable = returnVal.responseText; //ResponseText or whatever you need from the request...
this.Popup(this.MyVariable);
},
DoSomethingSmart: function() {
//Something smart
},
Popup: function(message) {
alert(message);
}
};
var TestClassInstance = new TestClass();
TestClassInstance.DoAjaxCall();
Related
I am having a problem, or perhaps a lack of understanding, with the jQuery execution order of $.get() function. I want to retrieve some information from a database server to use in the $.ready() function. As you all know, when the get returns, it passes the data to a return handler that does something with the data. In my case I want to assign some values to variables declared inside the ready handler function. But the problem is, the return handler of $.get() does not execute until after ready has exited. I was wondering if (a) am I doing this right/is there a better way or if (b) there was a way around this (that is, force the get return handler to execute immediately or some other fix I'm not aware of). I have a feeling this is some closure thing that I'm not getting about JavaScript.
As per request, I'll post an example of what I mean:
$(function() {
var userID;
$.get(uri, function(returnData) {
var parsedData = JSON.parse(returnData);
userID = parsedData.userID;
});
});
So as you can see, I'm declaring a variable in ready. Then using a get call to the database to retrieve the data needed. Then I parse the JSON that is returned and assign the userID to the variable declared before. I've tested it with a couple alerts. An alert after the get shows userID as undefined but then an alert in get's return handler shows it to be assigned.
$.get() is asynchronous. You have to use a callback to fill your variable and do the computation after the request is complete. Something like:
$(document).ready(function(){
$.get( "yourUrl", function( data, textStatus, jqXHR ) {
var myData = data; // data contains the response content
// perform your processing here...
registerHandlers( myData ); // you can only pass "data" off course...
});
});
// your function to register the handlers as you said you need to.
function registerHandlers( data ) {
// registering handlers...
}
$.get is an ajax request. A in AJAX stand for asynchronous, so script won't wait for this request to finish, but instead will proceed further with your code.
You can either use complete callback or you can use $.ajax and set async to false to perform synchronous request.
The $.get() function executes an async httprequest, so the callback function will be executed whenever this request returns something. You should handle this callback outside of $.ready()
Maybe if you explain exactly what do you want to do, it would be easier to help!
Are you looking for something like:
$(document).ready(function(){
var variable1, variable 2;
$.get('mydata.url', function(data){
variable1 = data.mydata1;
variable2 = data.mydata2;
});
});
If you declare the variables first, then you can set their values within the get call. You can add a function call at the end of the get handler to call a separate function using these values? Without some kind of example, its hard to go into any more detail.
Without seeing the full code, my guess is that you should declare your variable outside $.ready; initialize it in ready for the initial page load; then update it from the get callback handler.
for example
var x = ""; // declaration
$(document).ready(function() { x = "initial value"; });
$.get(...).success(function() { x = "updated from ajax"; });
I have a button that when clicked calls a function - this function does some asynchronous Ajax and alerts a messagebox when the Ajax has returned. I do not want the user clicking on the button multiple times - if he clicks on the button when the Ajax has not returned then an error message should be alerted.
I know that this can be easily done using a global boolean variable (set it initally to true, make the ajax call and set it to false - set it again to true when the ajax returnes - check if the global is false when the user clicks the button). Also it can be done similarly if instead of the window/global object I use another global object containing the function and the boolean
However, I do not like very much the above methods - I think that they are a little old-school-Javascript. I was wondering if there was a more elegant way to do it, for instance using JS closures !
Using this method, your variable will not leak to the global scope. There's no way to manipulate this variable from outside the function:
var foo = (function(){
var pending = false;
return function foo(){
if(pending) return;
pending = true;
//...Code
//When finished:
pending = false;
}
})()
Others may suggest setting a property of the function, but this property can easily be adjusted from outside, which is not desirable.
var callback = (function()
{
var executing = false;
function yourfunc()
{
if(executing) return; // exit if pending
executing = true;
// here send the request
// edit: when the ajax response has returned
xxxx.onreadystatechange....{
// do what you need to do with ajax data
executing = false;
};
}
return yourfunc;
})();
callback();
Anonymous function has the form
(function(){}))()
The last () provides the parameters for the anonymous function.
In the above sample script ( by wes ) returns error as callback is not a defined function. Rob's method using closure sounds good.
Cheers.. Sree
i want to pass a function to another function as a parameter.
I want to do that because the latter function calls an async Jquery method and AFTER that gives a result back, i want some javascript code executed.
And because this function is called from multiple places, i want the code to execute (after the async Jquery code gets executed) to be passed in the function.
Makes sense? i hope :)
Now what is see is that the order in which the code is executed is noth what i want.
I've simplified the code to this code:
$('#AddThirdParty').click(function() {
var func = new function() {
alert('1');
alert('2');
alert('3');
}
alert('4');
LoadHtml(func);
alert('5');
});
function LoadHtml(funcToExecute) {
//load some async content
funcToExecute;
}
Now what i wanted to achieve (or at least what i thought would happen) was that alert4 would fire, then the loadhtml would fire alert1, alert2 and alert3, and then the code would return to alert5.
But what happens is this: alert1, alert2, alert3, alert4, alert5.
Does anyone know what i'm doing wrong and why this is the order in which the code is executed?
It looks like the alert1..alert3 gets executed when i define the new function(), but why doesn't it ALSO get executed when i call it from the LoadHtml function?
$('#AddThirdParty').click(function() {
var func = function() { // NOTE: no "new"
alert('1');
alert('2');
alert('3');
}
alert('4');
LoadHtml(func);
alert('5');
});
function LoadHtml(funcToExecute) {
//load some async content
funcToExecute(); // NOTE: parentheses
}
Two errors: the syntax for anonymous functions does not include the keyword new; and JavaScript requires parentheses for function calls, even if functions do not take any arguments. When you just say funcToExecute, that is just a variable giving its value in a context where nothing is using that value (kind of like writing 3; as a statement).
You might notice that you already know how to use anonymous functions: you did not write $('#AddThirdParty').click(new function() ...);
$('#AddThirdParty').click(function() {
var func = new function() {
alert('1');
alert('2');
alert('3');
}
alert('4');
LoadHtml(func);
alert('5');
});
function LoadHtml(funcToExecute) {
//load some async content
funcToExecute;
}
The new keyword creates an object from the function. This means the function (which is anonymous) gets called immediatly. This would be the same as
var foo = function() {
alert("1");
alert("2");
alert("3");
}
var func = new foo();
This means your creating a new object (not a function!) and inside the constructor your alert 1,2,3. Then you alert 4. Then you call LoadHtml which does nothing, then you alert 5.
As for
funcToExecute;
The funcToExecute is just a variable containing a function. It actually needs to be executed.
I'm developing a javascript code to run on an embedded device using the ANT Galio browser.
Ideally, I'd like the code to make a get request to another server. After that get request is made, the page would not allow the user to submit another get request, until a response had been received from the previous get request.
For some reason sometimes I am receiving a readyState of 4 almost instantly. It is as though it is evaluating the previous XmlHttpRequest object and not the new one. What am I doing wrong?
<script type="text/javascript">
var fail= function (env, resp, stat) {
alert(resp);
};
var succ= function (env, resp) {
};
var canScan = true;
/* start scan */
function scan (name) {
if (canScan) {
//deactivate button
deactivateScanButtons();
//let server know
ajax = new XMLHttpRequest();
var scanUrl = 'http://19X.1XX.X.XX:8080/scan/' + name
ajax.open('GET', scanUrl, true);
ajax.onreadystatechange = function() {
if (ajax.readyState==4) {
//allow button to work again
activateScanButtons();
alert("ready state 4");
};
};
ajax.send();
//initiate scan
xrxScanInitiateScan(
'http://127.0.0.1',
"ftp.xst",
false,
succ,
fail);
}
}
function deactivateScanButtons () {
// canScan = false;
var indicator = document.getElementById('buttons');
indicator.style.visibility = "hidden";
}
function activateScanButtons () {
// canScan = true;
var indicator = document.getElementById('buttons');
indicator.style.visibility = "visible";
}
</script>
3 suggestions:
To avoid any caching on the client side, add a randomly generated number, or the current timestamp to the request querystring.
As Yoni said, initiate your XMLHttpRequest object with var keyword.
For each request, save the current timestamp within a global variable. In onreadystatechange, call activateScanButtons only if the global timestamp matches the corresponding timestamp of that given request. This way, only the latest request will be able to call activateScanButtons.
You define the ajax object in scan function without the var keyword before it. This means that it is a global object, not local. Afterwards, you have a closure - you refer to that variable in the onreadystate callback function.
I find it hard to track exactly what's going on there, but I agree with you, as you say in your question, that the callback is not using the ajax object the way you expect. You say it happens sometimes - does it happen when you make two requests almost simultaneously (double-click a button or otherwise trigger the two get requests very fast)?
I suggest that you use the var keyword before defining the ajax object. Even better, try using this in the callback function instead of referring to the ajax object by name. If it works, you have spared yourself of one closure.
i have this block of code:
$(document).ready(function() {
//<![CDATA[
var who;
FB_RequireFeatures(["Api"], function(){
var who = api.get_session().uid;
alert(who);
});
alert("the uid is: "+who);
//]]>
});
the problem:
the code outside the FB_RequireFeatures block is executing before the one inside it.
due to which the value of who is coming out to be undefined.
what am i doing wrong?
The FB_RequireFeatures function appears to be making an asynchronous call, so you're not doing anything wrong, that's just the way it works - the alert is called before the request comes back.
You must design your code in a way that the code that depends on the result of the FB_RequireFeatures functions are called only after the request completes. You can call another function inside the callback, for example:
var who;
$(document).ready(function() {
FB_RequireFeatures(["Api"], function() {
who = api.get_session().uid;
doSomeOtherStuff();
});
});
function doSomeOtherStuff() {
alert("the uid is: " + who);
}
Now the doSomeOtherStuff function is called only after the FB_RequireFeatures function finishes, and you should do all following code inside the doSomeOtherStuff function – which you can name to be anything you want, obviously.
I moved the who variable out of the ready block to keep it in scope for the doSomeOtherStuff function, and removed the var from the inner function so that you're referencing the original variable instead of creating a new one, otherwise it's the same.
You're making a new local who variable. Remove the var from the place where you set who. Also, you can't reference who until the callback to the FB_RequireFeatures function is run.