Reputation: 1876
I'm a rookie in node.js so I apologize if my question is immature .Basically I'm trying to iterate thorough an array of values for performing certain operation but I don't know why I'm getting the values as undefined
.
Code:
for (var i = 0; i < array.length; ++i) {
console.log(array[i]); //At this point I'm getting values without any problem
var sQ = {
_tag: array[i],
_pid: data.change_caption_post_mail,
time: data.change_caption_post_time,
};
db.collection('tags')
.find(sQ)
.count({}, function(error, numOfDocs) {
if (error) throw err;
console.log(array[i]);
//But here I'm getting values as **undefined**
});
}
Upvotes: 0
Views: 140
Reputation: 24156
Change var i = 0
to let i = 0
.
for (let i = 0; i < array.length; ++i) {
console.log(array[i]); //At this point I'm getting values without any problem
var sQ = {
_tag: array[i],
_pid: data.change_caption_post_mail,
time: data.change_caption_post_time,
};
db.collection('tags')
.find(sQ)
.count({}, function (error, numOfDocs) {
if (error) throw err;
console.log(array[i]);
});
}
N.B. When the callback function (function (error, numOfDocs) {...}
) is executing the for loop is finished. if we declare var i
then i
is equal to array.length
when executing callback so, array[array.length] returns undefined. One of the solutions is to use ES6 let
which will create callback's block scope i
for each iteration.
Upvotes: 2
Reputation: 379
The problem is that in your callback function, the reference of i remains only one which is already changed by the time callback is called.
you can change your code to
for (var i = 0; i < array.length; ++i) {
(function(){
var index = i;
console.log(array[index]); //At this point I'm getting values without any problem
var sQ = {
_tag: array[index],
_pid: data.change_caption_post_mail,
time: data.change_caption_post_time,
};
db.collection('tags')
.find(sQ)
.count({}, function(error, numOfDocs) {
if (error) throw err;
console.log(array[index]);
//But here I'm getting values as **undefined**
});
})();
}
it should work fine with this tweak
Upvotes: 0
Reputation: 56754
Replace
var i = 0; i < array.length; ++i
by
let i = 0; i < array.length; i++
and you're done.
for (let i = 0; i < array.length; i++) {
console.log(array[i]); //At this point I'm getting values without any problem
var sQ = {
_tag: array[i],
_pid: data.change_caption_post_mail,
time: data.change_caption_post_time,
};
db.collection('tags')
.find(sQ)
.count({}, function(error, numOfDocs) {
if (error) throw err;
console.log(array[i]);
// i will be array.length here in all your callbacks
});
}
scope
Check this example to understand the problem:
var
creates function scope
var funcs = []
for (var i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i)
})
}
funcs.forEach(function(func) {
func()
})
While you might expect this forEach
loop to result in number 0
to 9
being printed, instead you get ten times 10
. The cause of this is the variable i
being declared using var
keyword, which creates a function scope
that leads to each function
in funcs
holding a reference to the same i
variable. At the time the forEach
loop is executed, the previous for
-loop has ended and i
holds 10 (9++ from the last iteration).
Compare how ES6's let
, which creates block scope
instead of function scope
, behaves in this regard:
let
(ES6 or officially ES2015) creates block scope
:var funcs = []
for (let i = 0; i < 10; i++) {
funcs.push(function() {
console.log(i)
})
}
funcs.forEach(function(func) {
func()
})
Because let
creates block scope
, each iteration of the for
loop has its "own" variable i
.
IIFE
wrapperIf you need an ES5 solution, an IIFE (immediately invoked function expression) wrapper would be the way to go:
var funcs = []
for (var i = 0; i < 10; i++) {
funcs.push((function(value) {
return function() {
console.log(value)
}
}(i)))
}
funcs.forEach(function(func) {
func()
})
Here, i
is passed as a parameter to each function which stores its own copy value
.
for..in
loops:var funcs = [],
obj = {
first: "first",
last: "last",
always: "always"
}
for (var key in obj) {
funcs.push(function() {
console.log(key)
})
}
funcs.forEach(function(func) { // outputs: "always", "always", "always"
func()
})
Again, all functions in funcs
hold the reference
to the same key
because var key
creates a function scope that lives outside of the for..in
loop. And again, let
produces the result you'd probably rather expect:
var funcs = [],
obj = {
first: "first",
last: "last",
always: "always"
}
for (let key in obj) {
funcs.push(function() {
console.log(key)
})
}
funcs.forEach(function(func) {
func()
})
Also compare the excellent (!) book
Nicholas C. Zakas: "Understanding ES6", no starch press, p. 8-9.
from which the examples were taken.
Upvotes: 6