Why can't I update variables in this JavaScript class? [duplicate] - javascript

This question already has answers here:
How to access the correct `this` inside a callback
(13 answers)
Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference
(7 answers)
Closed 1 year ago.
I'm writing a JavaScript class for an object that will get data via ajax on creation. The object and its retrieved data will then be available for use later on.
The problem is that the variables don't update with new values from the ajax call. They stay at whatever values they were initiated at.
Here's a simplified example of my code:
class Foo {
constructor() {
this.bar = 0;
this.makeAjaxCall();
}
makeAjaxCall() {
jQuery.get(
// ajax logic
)
.done(
function(response) {
this.bar = response.bar;
}
);
}
}
var bax = new Foo();
console.log(bax.bar); // outputs 0
I've confirmed that the ajax call occurs, both via the network monitor and by console logging the response variable. It's just that the change of variable value doesn't "stick". This also occurs with code later on, so I don't think the issue is me logging before the ajax call completes.
I suspect this is related to late bindings in JavaScript, but I haven't been able to wrap my head around that. Here's what I tried in that realm (it didn't work):
var bax_unbound = new Foo();
var bax = bax_unbound.bind(Foo);
console.log(bax.bar); // still outputs 0

Related

Get data asynchronously JavaScript within for loop [duplicate]

This question already has answers here:
How do I return the accumulated results of multiple (parallel) asynchronous function calls in a loop?
(5 answers)
Closed 6 years ago.
I have this function
function add_cnh(arr_clelem){
var id=arr_clelem.getElementsByClassName("present")[0].id;
var date=arr_clelem.getElementsByClassName("present")[0].getAttribute('date');
var tt_entry= arr_clelem.getElementsByClassName("present")[0].getAttribute('tt_entry');
//new Ajax.Updater('register', '/some_url', { method: 'get' });
new Ajax.Request('/attendances/new',
{
parameters:'id='+id+'&date='+date+'&timetable_entry='+tt_entry+'&subject_id='+subject_id,
asynchronous:true,
evalScripts:true,
method:'get'
/*onSuccess: function(transport) {
var response = transport.responseText || "no response text";
alert("Success! \n\n" + response);
}*/
}
)
var ret=modal_data;
// $$('.MB_close').invoke('observe', 'click', _deinit);
return ret;
}
This function takes html-elements-object as an argument and basically render a modal-box and that modal box contain a form -elements which i need to store inside an array. The variable modal_data contains the elements which I require and its a global variable define in another file.
My problem is
This is a very old project using many JavaScript frameworks and libraries which date back to 2006 the library responsible for opening the model box itself is deprecated as can be seen here
And somehow I don't want to get into server side so I am using a for loop something like this
for(var i=0; i<arr_of_elements.length, i++)
{
my_arrvar[i]=add_cnh(arr_of_elements[i]);
}
Now with each itteration since I want the modal box to get closed and store the data within 'my_arrvar' which is somehow not possible as the call is asynchronous in nature and I've used closures and callbacks but no success. I don't want to use any sort of timer. So this is how it goes
Call the function and get data for each call and remove the modal box by id.
Also can this be used somehow and if then how?
You have to pass in ajax request asynchronous:false instead of true. Otherwise its not possible by another way.
Other Way using jQuery
The easiest way is to use the .ajaxStop() event handler:
$(document).ajaxStop(function() {
// place code to be executed on completion of last outstanding ajax call here
});
See jQuery Event handler

How does the scope of JavaScript variables work in this script? [duplicate]

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 7 years ago.
I have this code but I am unable to understand why when I print 'globalData' first it prints all values fine but later when I print it again it print just an empty array? I'm new to JavaScript and jQuery.
<script>
var globalData = [];
$( document ).ready(function() {
$.get( "http://....", function( data ) {
globalData.push(data[i]);
.
.
.
});
console.log(globalData); //["fg", "wedsd", "hjkyu"]
});
console.log(globalData); //[]
</script>
With $( document ).ready() you only define an anonymous function that is called as soon as the document structure is build successfully. Then you print globalData which is empty at this point. If your defined function gets called, it initializes the globalData and you see the values.
All in all, because the callback function you define gets called asynchronously and the definition itself doesn't block and returns immediately (even if the DOM is not loaded already), you print globalData before it gets filled with values in the callback.
The problem is even unrelated to the Ajax request invoked with $.get even so you run into a similar situation there. But you would get the same problem if you simply fill the array inside the ready callback.

Get Data out of JQuery Function $.getJSON [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 8 years ago.
This is more of a basic JQuery/Javascript question: How do I get data out of the getJSON function?
The following code works great until the function ends. I have several getJSON that will need to run one-after-the-other and I would like to avoid nesting them if I can:
//Using the Web API to pull all the information out for the current ORG ID
$.getJSON('http://localhost:8081/dhis/api/organisationUnits/' + vuCurrentFacilityID + '.json', function (data) {
//alert(data.message);
console.log(data);
vuCurrentFacilityName = data.name;
vuParentFacilityID = data.parent.name;
});
alert(vuCurrentFacilityName); //does not work
What Gotcha* am I missing?
This question is very similar to the following.
if you call $.getJSON() you are entering an asynchronous context, and everything after it gets executed immediately. So your alert is called before the server responds.
use deferreds instead
var promise = $.getJSON( ... );
promise.done(function() {
alert("yeah");
});

Javascript Scope: variable not defined [duplicate]

This question already has answers here:
How to return AJAX response Text? [duplicate]
(2 answers)
How do I return the response from an asynchronous call?
(41 answers)
Closed 9 years ago.
In the following code the alert inside the function works fine, but the second has variable undefined, and yet I have delcared the variable outside of the function. Why is this?
var data = [];
$.post(
'matchEngine.php',
function(data) {
for (var i = 0, len= data.length;i <len; i++) {
for ( h = 0, len2= data[i].length;h <len2; h++) {
data[i][h][0]=(data[i][h][0])*30;
data[i][h][1]=(data[i][h][1])*30;
data[i][h][3]=data[i][h][3].replace(/\"/,"");
}
}
alert(data[0][0][0]);
}
);
alert(data[0][0][0]);
if you are suffering a similar problem the following How to return the response from an AJAX call? has the definitive explanation and answer.
The reference data in the function parameter and outside the function are different variables. In first case, it is in global scope, and in the second it in the local scope..They are completely different.
The example illustrates the issue....
var data=2;//this
function fun(data){ //and this are different
alert(data);
}
var data2=3;
fun(data2);
You can try this:
var data = [];
var myRequest = $.post(
/* your stuff */
);
myRequest.done(function() { alert(data[0][0][0]); })
As pinkpanther noted, the local data variable inside your $.post callback is not the same variable as the data variable outside the function.
Additionally, since $.post is asynchronous, you need to either pass it a callback or use the deferred object that it returns to access the response.
$.post('matchEngine.php').then(function(data){alert(data)})
for example, if you want to be able to pass the response around to other functions, you can do something like:
function doPost(url){
return $.post(url);
}
function processResponse(response) {
alert(response);
}
responsePromise = doPost("matchEngine.php");
responsePromise.then(processResponse);
As an aside, I recommend using $.ajax instead of $.post if you are going to use the callback style instead of promises. The reason being that $.ajax provides an error callback while $.post does not.

javascript OOP Confusion [duplicate]

This question already has answers here:
Closed 11 years ago.
Possible Duplicate:
Javascript OOP return value from function
I have a class defined like this
function SocialMiner(tabUrl)
{
var verbose=true;
var profileArray=new Array();
this.tabUrl=tabUrl;
this.getTabUrl=function(callback)
{
chrome.tabs.getSelected(null, function(tab)
{
callback(tab.url);
});
}
this.setTabUrlValue=function(pageUrl)
{
this.tabUrl=pageUrl;
console.log("22"+this.tabUrl); //this statement shows url correctly
}
}
When I call this method like these
miner.getTabUrl(miner.setTabUrlValue);
miner.logToConsole("1"+miner.tabUrl); //This statement returns undefined
The console.log inside callback correctly outputs url , however, the tabUrl property of miner ojbect is undefined , as seen in second console.log. Why is it so ?
The solution is to save a reference to this within the constructor (available later on via closure):
var that = this; //in the top of the SocialMiner constructor function
and in setTabUrlValue use:
that.tabUrl=pageUrl;
I suspect running a method as a function (callback) loses scope, i.e. doesn't know of any this anymore. In other words, it runs within the scope of the constructor, not as a method of the instance using it. A variable referencing this in the constructor scope is available to the function, and that points to the right this on instance creation.
You could also force callback to run in the current instance scope like this:
callback.call(this,tab.url);
In that case you can leave this.tabUrl=pageUrl; as it is.
This is an simplification of your code. The methods return this to be able to directly reference a property of the instance (see console.log last line):
function Some(){
var that = this; // note: not used in this example
this.getA = function(callback){
someval = 'foobar';
callback.call(this,someval);
return this;
};
this.getB = function(val){
this.val = val;
return this;
};
}
var some = new Some;
console.log( some.getA(some.getB).val ); //=> foobar
Taking a look # your code again, I think you're loosing scope twice, because callback is called from within another callback. That's why I think your code on that spot should be:
chrome.tabs.getSelected(
null,
function(tab) {
callback.call(that,tab.url); //< use that here
}
);
Furthermore, in you code # github, I don't see any instantiation of the miner instance.
this is a tricky beast in JavaScript and as others have pointed out is the key to the issue. The problem with using this everywhere is that it's value can change depending on who/where the function is called from (for example, see the call and apply methods in JavaScript). I'm guessing that if you wrote the value of this to the console in the the callback from the chrome.tabs.getSelected function you'd find it isn't your miner any more.
The solution is to capture a reference to the this that you're actually interested in when you know for sure it's the right one & then use that reference from then on. Might make more sense to see it commented in-line in your example:
function SocialMiner(tabUrl)
{
//At this point we know "this" is our miner object, so let's store a
//reference to it in some other (not so transient) variable...
var that = this;
var verbose=true;
var profileArray=new Array();
this.tabUrl=tabUrl;
this.getTabUrl=function(callback)
{
chrome.tabs.getSelected(null, function(tab)
{
//at this point "this" is whatever the "chrome.tabs.getSelected"
//method has decided it is (probably a reference to the tab or something)
callback(tab.url);
});
}
this.setTabUrlValue=function(pageUrl)
{
//because this can be called from anywhere, including the chrome callback
//above, who knows what "this" refers to here (but "that" is definitely
//still your miner)
that.tabUrl=pageUrl;
console.log("22"+that.tabUrl);
}
}
You can see how much this shifts around in libraries that use callbacks heavily like jQuery, where often this is set to convenient values, but certainly not the same this that was logically in scope when you made the initial call.
EDIT: Looking at the full source (& example) you posted, this is just a timing issue where obviously the chrome.tabs.getSelected is returning asynchronously after your "second" call to log goes through...
console.log("5");
miner.getTabUrl(miner.setTabUrlValue); //setTabUrlValue is logging with '22'
console.log("6");
miner.logToConsole("1"+miner.tabUrl);
console.log("7");
// Output:
5
6
1 undefined //the chrome.tabs.getSelected hasn't returned yet...
7
22 http://url //now it has (so if you tried to use miner.tabUrl now you'd be all good...
The solution is to put all the stuff after the get/set into the callback, since you don't want anything happening until after that tabUrl is finished being set... so something like this:
console.log("5");
miner.getTabUrl(function(pageUrl) {
miner.setTabUrlValue(pageUrl);
console.log("6");
miner.logToConsole("1"+miner.tabUrl);
console.log("7");
});
Hopefully that will see you getting your results in the order you expect them.
I think this happens because closure vars do not survive a function call.

Categories