NodeJS Local variable in callback [duplicate] - javascript

This question already has answers here:
Javascript infamous Loop issue? [duplicate]
(5 answers)
Closed 5 years ago.
I,
I know, there is a lot information about this on SO, but I didn't find the right answer for my situation.
I have this piece of code:
for(var i=0; i < shop.collections.length; i++){
if(!shop.collection[i].active){
var data = {
name: shop.collection[i].name,
visible: true
};
myOwnService.createCollection(data, accessToken, function(err, response, body){
shop.collections[i].serviceId = body.id;
})
}
My problem is that shop is undefined in the myOwnService.createCollection() service. What is the right way to access the shop variable in the callback, so how I can update the object so I can save the result?

shop is in the parent scope of the myOwnService.createCollection function hence it is accessible inside it.
Only issue in above code is i used in callback which will always be the last value as it will be executed after event loop.
Use IIFE wrapper around myOwnService.createCollection so that scope of i could be passed.
for (var i = 0; i < shop.collections.length; i++) {
if (!shop.collection[i].active) {
var data = {
name: shop.collection[i].name,
visible: true
};
(function(i) {
myOwnService.createCollection(data, accessToken, function(err, response, body) {
shop.collections[i].serviceId = body.id;
});
})(i);
}
}

It should be accessible inside where you try to access. If you are tying Chrome debugger to see if it has the value, Chrome debugger sometime mess up and shows it like is not available.
https://bugs.chromium.org/p/v8/issues/detail?id=3491

Related

Variable is lost when passed as a parameter in setTimeout() [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
I have a problem with calling a function with a parameter inside a setTimeout function. Basically I'm trying to make a small online game, where I create a queue of commands and then execute them one at a time (each takes some time to show a visualization).
Unfortunately it seems that I cannot pass any variable as a parameter inside the setTimeout(). Although the variable does exist when I call the function it does not exist later when it is executed. The function doesn't keep track of the passed value.
Is there any solution to this? Thanks a lot for any help. Here is a code I use:
function executeCommands() {
var commands = document.getElementsByClassName("cmdplace");
var timeout = 0;
for (i = 0; i < commands.length; i++) {
console.log(commands[i].childNodes[0]); //variable exists
setTimeout(function() {go(commands[i].childNodes[0]);}, timeout+=400); //Uncaught TypeError: Cannot read property 'childNodes' of undefined
console.log(commands[i].childNodes[0]); //variable still exists
}
}
function go(command) {
//do somethig based on the passed command
}
When your functions are invoked, i is equal to commands.length and commands[i] is undefined.
They are capturing the variable i, not its value.
When they execute, they get out of i the actual value, but so far it has reached commands.length (that is the condition used to break your loop).
You can do something like this to work around it:
setTimeout(function(j) {
go(commands[j].childNodes[0]);
}.bind(null, i), timeout+=400);
Or this:
setTimeout((function(j) {
return function() {
go(commands[j].childNodes[0]);
};
})(i), timeout+=400);
Note also that, as you defined it, i is a global variable.
As mentioned in the comments by #PMV, there's a much easier way in modern JavaScript (if that's an option for you).
Just use a let statement as it follows:
for (let i = 0; i < commands.length; i++) {
// do whatever you want here with i
}
This will ensure that each iteration gets a new variable named i and you can capture it as in the original code.
You need to make a distinct copy of each item. By the time the setTimeout runs the loop has already finished.
var timeout = 0;
function executeCommands() {
var commands = document.getElementsByClassName("cmdplace");
for (i = 0; i < commands.length; i++) {
go(commands[i]);
}
}
function go(command) {
setTimeout(function() {
console.log(command);
}, timeout += 400);
}
executeCommands();
<ul>
<li class="cmdplace">A</li>
<li class="cmdplace">B</li>
<li class="cmdplace">C</li>
<li class="cmdplace">D</li>
</ul>

Get original variable value [duplicate]

This question already has answers here:
JavaScript closure inside loops – simple practical example
(44 answers)
Closed 6 years ago.
I'm trying to use a variable value in a callback function, but am unable to get the original value of the variable when written into the function.
for (i=0; i<2; i++) {
if ($.type(picker[i]) == "object") {
var id = picker[i].$root[0].id;
picker[i].on({
set: function() {
alert("#" + id + " .picker__holder");
},
});
// other code..
}
}
This code is supposed to go throught the picker object twice, where the id value is first "datepicker" and on the second run "datepicker--2" but when the callback is called the id is always the second value "datepicker--2" where for the picker[0].on() callback i'd need it to be the first one ("datepicker").
I assume this is because the value of the variable is read when the callback code is executed, and by that time it has the last value.
What would be a reasonable workaround for this?
Best,
Alari
You need to refactor the inner asynch call into a function.
for (i=0; i<2; i++) {
if ($.type(picker[i]) == "object") {
var id = picker[i].$root[0].id;
// other code..
asynhCall(picker, i, id);
}
}
function asynhCall(picker, i, id)
{
picker[i].on({
set: function() {
alert("#" + id + " .picker__holder");
},
});
}
This is because by the time asynch-callback handler was executed, only id will have only the last value would be used (since because of event-loop model).

Javascript closure access with callbacks inside loop

what shall I do to make the last row of code return a value?
$scope.runActionwithObjects = function() {
for (var i = 0; i < $scope.Objects.length; i++) {
console.log($scope.Objects[i]); //$scope is accessible
$http.get($scope.Objects[i]["Commit"]).success(function (data) {
console.log($scope.Objects[i]);//return undefined
The problem is due to asynchrony of ajax requests.
When the success callback is executed, your loop has already finished and the i variable is already equal to $scope.Objects.length.
Try forEach. This function will create different closures for items in the array.
$scope.Objects.forEach(function(currentObject){
console.log(currentObject); //$scope is accessible
$http.get(currentObject["Commit"]).success(function (data) {
console.log(currentObject);
});
});
The reason your $scope.Objects[i] is undefined because the variable i is always = $scope.Objects.lenth + 1, for example you got 5 elements, the i will be 6, because the at the time of callback, it already got the last value.
One solution is to bind needed object to that method, so we can access it via this(we can not reference directly by closure to ref variable, because it's still stored the last item), for example:
for (var i = 0; i < $scope.Objects.length; i++) {
var ref = $scope.Objects[i];
// console.log($scope.Objects[i]); //$scope is accessible
var successCallback = (function (data) {
console.log(this);//return the ref
}).bind(ref);
$http.get('').success(successCallback);
}
}

Javascript - Closures - Lexical Scoping - How to include a loop variable data in nested function? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Javascript infamous Loop problem?
I have the following code:
function test() {
var columns = options.columns;
for (var i =0; i < columns.length; i++) {
if (columns[i].type === "number") {
var field = columns[i].field;
columns[i].footerTemplate = function(data) { return buildFooter(data, field); };
}
}
}
function buildFooter(data, field) {
alert(field);
}
A library function calls the footerTemplate function (which in turn call buildFooter). The alert in buildFooter states that field is always the same value (the last value iterated in for loop of test.) How can buildFooter be called with the appropriate field value (i.e.
columns[0].footerTemplate = function(data) { return buildFooter(data, columns[0].field);}
and
columns[1].footerTemplate = function(data) { return buildFooter(data, columns[1].field);}
and so on...
Javascript does not scope variables inside logical blocks (loops, ifs). That field variable is shared across all the footerTemplate properties.
You can solve this by creating an inline function to create some scope:
for (var i =0; i < columns.length; i++) {
if (columns[i].type === "number") {
(function () {
var field = columns[i].field;
columns[i].footerTemplate = function(data) {
return buildFooter(data, field);
};
})();
}
}
}
Try:
columns[i].footerTemplate = (function(field) {
return function (data) {
buildFooter(data, field);
};
})(field);
It immediately executes a function that returns a new function that has the correctly bound field variable. It's definitely a scope issue that is a problem with loops, so using an immediately invoked function to create a new scope and making the correct variables available. This is definitely a duplicate of Javascript infamous Loop issue? though
Javascript is function-scoped language, so your decleration of
field
variable inside of for loop is the same as you declared it outside of foor loop, you're just overwriting same variable over and over again, there's no memory allocated for field variable in every loop, it's one same memory space that's being written to.

Maintain a state of a variable in an asynchronus function

I don't know if there is a term for the thing i ask about, so i'll try to illustrate it:
I Got a function that takes an array of contacts, and loop them through to update their status on the website. The 'something.getStatus' function does not return anything, but upon successful completion, it gives the status (online/offline/etc.) of the user.
The problem here is, when the function inside is called, the variable user_id is the last one in the array for all the function calls, making it impossible to know how have what status.
function gotGroupContacts(contacts) {
for ( i = 0; i < contacts.length; i++) {
var user_id = contacts[i];
something.getStatus(user_id, function(resp) {
updateLinkStatus(user_id, resp.status, resp.statusMessage);
}, getStatusErrorHandler);
}
}
Is there any way i can 'lock' the variable, or otherwise pass it to the function within?
enclose your getStatus method call in a closure, like so
for ( i = 0; i < contacts.length; i++) {
var user_id = contacts[i];
(function(uid) {
something.getStatus(uid, function(resp) {
updateLinkStatus(uid, resp.status, resp.statusMessage);
}, getStatusErrorHandler);
}(user_id))
}
user_id is passed into the anonymous self-executed function, and this ensure to have the right value
if your script doesn't need to be compatible with IE <9,
you can write like this:
function gotGroupContacts(contacts) {
contacts.forEach(function(user_id){
something.getStatus(user_id, function(resp) {
updateLinkStatus(user_id, resp.status, resp.statusMessage);
}, getStatusErrorHandler);
});
}
NOTICE: forEach method can be easily ported to IE, see this: https://developer.mozilla.org/en-US/docs/JavaScript/Reference/Global_Objects/Array/forEach

Categories