I'm trying to execute a setTimeout() function inside of a for loop in Javascript, but I am getting the error "shape is undefined", even though it is defined, and I'm passing that as a parameter in the function within the setTimeout() call. The function works just fine if I delete the setTimeout enclosure.
Why am I getting this error and how can I fix it?
Thanks!
function fadeShapes(layer, movement, opacity, speed) {
var shapes = layer.getChildren();
for(var n = 0; n < shapes.length; n++) {
var shape = layer.getChildren()[n];
setTimeout(function(shape){
shape.transitionTo({
alpha: opacity,
duration: speed
});
}, 100);
}
}
JavaScript does not have block scope so all of your timeout functions are pointing at the same variable shape
, which after the loop finishes points to an undefined index of your array. You can use an anonymous function to emulate the scope you're looking for:
for(var n = 0; n < shapes.length; n++) {
var shape = shapes[n]; //Use shapes so you aren't invoking the function over and over
setTimeout((function(s){
return function() { //rename shape to s in the new scope.
s.transitionTo({
alpha: opacity,
duration: speed
});
};
})(shape), 100);
}
As you could tell by my issues getting the brackets matched up, this can be a little tricky. This can be cleaned up with ES5's Array.forEach
:
layer.getChildren().forEach(function(shape) { //each element of the array gets passed individually to the function
setTimeout(function(shape){
shape.transitionTo({
alpha: opacity,
duration: speed
});
}, 100);
});
forEach
is built into modern browsers but can be shimmed in Internet Explorer older browsers.
This is a common closure problem, here is the fixed code:
function fadeShapes(layer, movement, opacity, speed) {
var shapes = layer.getChildren();
for(var n = 0; n < shapes.length; n++) {
var shape = layer.getChildren()[n];
setTimeout((function (bound_shape) {
return function() { // return function!
bound_shape.transitionTo({
alpha: opacity,
duration: speed
});
};
}(shape)) // immediate execution and binding
, 100);
}
}
What happens in your code is that the for loop will run and n
functions will be scheduled from execution in 100ms
, but the value of shape
changes! So when your callback gets called shape
is the value of shapes[length-1]
(the last shape).
To fix it, you must 'close' the value using a closure. In this case a function that binds on the value of shape
and returns the function you want executed in 100ms
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With