Accessing global variable inside callback function [duplicate] - javascript

This question already has answers here:
How do I return the response from an asynchronous call?
(41 answers)
Closed 3 years ago.
I am facing an issue related to the variable scope. Initially set "successCount" and "failedCount" variables as '0' and incremented that value after each success/failure iteration. But after the iteration got the initial value only for those variables.
var successCount = 0;
var failedCount = 0;
var counter = 0;
var data = {};
for (var i = 0; i < 5; i++) {
updateField(data, (response) => {
if (response.status) {
successCount++;
} else {
failedCount++;
}
});
counter++;
}
Accessing variable outside the loop
if(counter === 5) {
console.log(successCount);// Value is still 0
console.log(failedCount);// Value is still 0
}
Any solution or what I am wrong with this code.
Thanks in advance

Since the variables are updated in the callback you need to make sure that your console.log is not called before your callback.
You can try to convert your update function into a promise to be able to run the console log after all the function are resolved.
var successCount = 0;
var failedCount = 0;
var counter = 0;
var data = {};
function asyncUpdateField(data) {
return new Promise((resolve) => {
updateField(data, (response) => {
if (response.status) {
successCount++;
} else {
failedCount++;
}
resolve();
});
});
}
const promisesUpdateField = [];
for (var i = 0; i < 5; i++) {
promisesUpdateField.push(asyncUpdateField(data));
}
Promise.all(promisesUpdateField).then(() => {
console.log(successCount);// Value is still 0
console.log(failedCount);// Value is still 0
});

As it seems, the function updateField may be asynchronous. That's why your console.log() is being called before the execution of that function and at which point, value of successCount and failedCount is 0.

Related

node.js async; counting callbacks

How does counting instances of a callback function work in node.js?
I was working on the 9th exercise of learnyounode (below the official solution).
As I understand it, the httpGet function is called three times, running through process.argv[2], [3] and [4]. But how could count ever === 3? Don't the individual functions just get to one? How does one call of httpGet know of the other ones?
var http = require('http')
var bl = require('bl')
var results = []
var count = 0
function printResults () {
for (var i = 0; i < 3; i++)
console.log(results[i])
}
function httpGet (index) {
http.get(process.argv[2 + index], function (response) {
response.pipe(bl(function (err, data) {
if (err)
return console.error(err)
results[index] = data.toString()
count++
if (count == 3)
printResults()
}))
})
}
for (var i = 0; i < 3; i++)
httpGet(i)
But how could count ever === 3?
count is defined outside of httpGet and thus its value is independent of the those function calls. count++ is the same as count = count + 1, i.e. every call to httpGet increases the value of count by 1. The third time the function is called, count's value will be 3.
We can easily replicate this:
var count = 0;
function httpGet() {
count++;
console.log('count: ', count);
if (count === 3) {
console.log('count is 3');
}
}
httpGet();
httpGet();
httpGet();
First, prefer not using var.
var is defined in the global scope so it’s value updated between calls
Read more about var here

Running functions using string name from array [duplicate]

This question already has answers here:
How to execute a JavaScript function when I have its name as a string
(36 answers)
Closed 5 years ago.
I have an array that has a bunch of function names. I am not storing the function in an object.
for (i = 0; i < view_functions.length; i++) {
view_functions[i]();
}
This doesn't work, any ideas?
Thanks.
The code you posted works, so perhaps the code that defines the functions isn't quite right. Here is a fully working example.
function yep() {
alert('Yep');
}
function again() {
alert('Again');
}
var view_functions = [
yep,
again
];
for (var i = 0; i < view_functions.length; i++) {
view_functions[i]();
}
If you wanted to get a bit of design help in your code, you could introduce a factory. This has the added benefit of making unknown function names explicit.
function yep() {
alert('Yep');
}
function again() {
alert('Again');
}
function functionFactory(name) {
switch (name) {
case 'yep':
return yep;
case 'again':
return again;
default:
throw `${name} not a known function in functionFactory`;
}
}
var view_functions = [
'yep',
'again'
];
for (var i = 0; i < view_functions.length; i++) {
var func = functionFactory(view_functions[i]);
func();
}
If you only have string names, I recommend not using string names... but you could go full-evil and use eval...
function yep() {
alert('Yep');
}
function again() {
alert('Again');
}
var view_functions = [
'yep',
'again'
];
for (var i = 0; i < view_functions.length; i++) {
var n = view_functions[i] + '()';
eval(n);
}
But why not store the actual functions in the array instead...

Undefined value from looping through json

In typescript/javascript I'm trying to fetch 'statute' from data object:
{_id: "31ad2", x: 21.29, y: -157.81, law: "290-11",....}
So I assign data.law to a variable. But, I'm getting typeerror cannot read property 'law' of undefined?
If I console log 'data.law' at line 11 or result[0] at the line 18 I get the correct value...
sectionsSuccess(res: Response) {
this.allSections = [];
this.sections = [];
this.loadingSections = false;
try {
let jsonRes = res.json();
this.jsonResLength = jsonRes.length;
for (var a = 0; a < this.jsonResLength; a++) {
let js = jsonRes[a];
js.bookmarked = this.server.isInBookmark(js);
this.allSections.push(js);
if (a < 15) {
this.sections[a] = this.allSections[a];
}
}
} catch (e) {
alert("Exception: " + e.message);
}
for (var i = 0; i < this.allSections.length; i++) {
this.allSections[i] = this.convertLocationDataToStatutes(this.allSections[i]);
}
// complete code added above
for (var i = 0; i < this.sections.length; i++) {
this.sections[i] = this.convertLocationDataToStatutes(this.sections[i]);
}
}
convertLocationDataToStatutes(data: any): any {
var self = this;
var chapterandsection = data.law; //line 11
var values = chapterandsection.split('-');
var chapter = values[0];
var section = values[1];
(self.server).getSection(chapter, section)
.map(response => response.json()).subscribe(result => {
return result[0]; // line 18
});
}
Your convertLocationDataToStatutes never returns anything, but you're using its return value. The result of calling a function with no return value is undefined. So the loop fills this.sections with a bunch of undefineds. That means the next time sectionsSuccess gets called, it will see undefined in this.sections[x], and accessing data.law on it will cause the error. So the problem will present the second time sectionsSuccess is called; your logging of data.law where you saw the value presumably was the first call to it.
The only return in convertLocationDataToStatutes is the one inside the subscribe callback. Presumably you meant to return something from convertLocationDataToStatutes itself.

How to make the return variable hold a new value after Increment/Decrementing it in a function?

Everytime I run this function, the p1_Balance will always reset back to 10 and will not hold the new value of an increment or decrement.
function Balance() {
var p1_Balance=10;
var x= Math.floor(10*Math.random());
if (x<5) {
p1_Balance=p1_Balance-1;
} else {
p1_Balance=p1_Balance+1;
}
return p1_Balance;
}
Pass p1_Balance into the function instead of initializing it each time the function is called with: var p1_Balance = 10;
p1_Balance should be declared outside the scope of the function (meaning not within the function itself). Otherwise, each time the function is called, the initializer that sets the value to 10 runs as well.
var p1_Balance=10;
function Balance(){ ...
You can use Javascript closures to create a function that does what you want, as you can see below:
var Balance = (function() {
var p1_Balance = 10;
return function() {
var x = Math.floor(10 * Math.random());
if (x < 5)
return p1_Balance += 1;
else
return p1_Balance -= 1;
};
})();
for (var i = 0; i < 10; i++)
console.log(Balance());
Alternatively, you will need to define the p1_Balance variable outside the function or pass it as an argument.
There could be several solutions:
one is declaring p1_Balance as a global variable.
var p1_Balance=10;
function Balance(){
var x= Math.floor(10*Math.random());
if (x<5) {
p1_Balance=p1_Balance-1;
}
else {
p1_Balance=p1_Balance+1;
}
return p1_Balance;
}
another is you could pass balance as a function parameter:
function Balance(p1_Balance){
var x= Math.floor(10*Math.random());
if (x<5) {
p1_Balance=p1_Balance-1;
}
else {
p1_Balance=p1_Balance+1;
}
return p1_Balance;
}
.....
value = Balance(10);// value=something that you want to change by that function.

how to get incremented value in for loop after callback function in javascript?

My Requirement:
I want to get the list of values using for loops. In for loop one iteration completed one time then the callback will send that list of values(array).
Once the first iteration completed second time loop value should be get incremented value.
For example : 5 values
after 5th iteration then loop is over. then second time loop should start with '0' but here it's starting with last incremented value. please help me to achieve this.
Below code is working fine for the first time.
Callback function:
$inventoryManagement.getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId(objectId,attributeId, function(objectAttributeBlockElement) {
//$scope.val = myOwnJ;
console.log(objectAttributeBlockElement);
});
Function:
var myOwnJ = 0;
// Getting ObjectId And AttributeId Using CellId For Normal Controls
var getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId = function(objectId,attributeId, callback) {
var objectAttributeBlockElement = [];// one array
try {
// iterate over the objectAttributes
for (var i = 0; i < pageObject.objects.length; i++) {
if (pageObject.objects[i].id == objectId) {
var name = "";
var labelName = "";
var dataTypeId = "";
for (;myOwnJ < pageObject.objects[i].objectAttribute.length;) {
name = pageObject.objects[i].objectAttribute[myOwnJ].name;// got the current label name
labelName = pageObject.objects[i].objectAttribute[myOwnJ].labelName;// got the current name
dataTypeId = pageObject.objects[i].objectAttribute[myOwnJ].dataTypeId;// got the current dataTypeId
objectAttributeBlockElement.push(name,labelName,dataTypeId);
callback(objectAttributeBlockElement, myOwnJ++);
return;
}
}
}
throw {
message: "objectId not found: " + objectId
};
} catch (e) {
console.log(e.message + " in getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId");
}
};
You could pass j as an additional function parameter, such as
var getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId = function(objectId, attributeId, j, callback) {
so it won't be a local variable. Then, instead of declaring it locally, use the following:
for (j = ((j === null) ? 0 : j); j < pageObject.objects[i].objectAttribute.length; j++) {
That way, if you call your function with j, you'll get it incremented after each call.
Another approach, which I won't recommend, would be making j a global variable by declaring it ouside your function instead of passing it as a parameter. That way you don't have to modify your function declaration at all. If you're up to that, I strongly suggest modifying the variable name cause j would be too generic for a global scope variable and it will cause trouble sooner or later: use something like myOwnJ and you'll be fine.
EDIT: Full source code (as requested by the OP):
var myOwnJ = 0;
// Getting ObjectId And AttributeId Using CellId For Normal Controls
var getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId = function(objectId,attributeId, callback) {
var objectAttributeBlockElement = [];// one array
try {
// iterate over the objectAttributes
for (var i = 0; i < pageObject.objects.length; i++) {
if (pageObject.objects[i].id == objectId) {
var name = "";
var labelName = "";
var dataTypeId = "";
if(myOwnJ < pageObject.objects[i].objectAttribute.length) {
name = pageObject.objects[i].objectAttribute[myOwnJ].name;// got the current label name
labelName = pageObject.objects[i].objectAttribute[myOwnJ].labelName;// got the current name
dataTypeId = pageObject.objects[i].objectAttribute[myOwnJ].dataTypeId;// got the current dataTypeId
objectAttributeBlockElement.push(name,labelName,dataTypeId);
callback(objectAttributeBlockElement, myOwnJ++);
return;
}
else {
myOwnJ = 0;
}
}
}
throw {
message: "objectId not found: " + objectId
};
} catch (e) {
console.log(e.message + " in getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId");
}
};
What you are looking for is a global variable for 'j'. Although this is discouraged to be used.
var j=0;
var getObjectNameAndAttributeAndDataTypeIdUsingObjectAndAttributeId =
function(objectId, attributeId, callback) {
//do your stuff
//increment j
j++;
}

Categories