Access values from dynamic array via another function - javascript

I have a function that sets bunch of setTimeout functions. All functions are added to Array funcs; nevertheless, when I try to stop them using stopplay().clearTimeout the array has no values.
How to access those functions and clearTimeout on them?
var funcs = new Array();
function playWithDelay(){
for (var i = 0; i < PlayDatesArray.length; i++) {
funcs[i] = createfunc(i);
}
for (var j = 0; j < PlayDatesArray.length; j++) {
funcs[j]();
}
}
function createfunc(i) {
return function() {
setTimeout(function(){
//my function
}, i*1500);
};
}
function stopplay(){
alert(this.funcs.count);
for (var i = 0; i< funcs.count; i++){
//things I tried
var tmpFunction = funcs[i];
//funcs[i].splice(i, 1);
clearTimeout(tmpFunction);
clearTimeout(funcs[i]);
funcs[i]=tmpFunction;
}
}

clearTimeout takes the id returned by setTimeout, not a reference to the function itself.
So what you want is (in ES5 code)
var timeouts = [];
function createfunc(i) {
return function() {
return setTimeout(function(){
//my function
}, i*1500);
};
}
// code to create the functions
function playWithDelay(){
for (var i = 0; i < PlayDatesArray.length; i++) {
timeouts.push(createfunc(i)());
}
}
// code to stop them
function stopplay(){
timeouts.forEach(clearTimeout);
}

You're accessing this.funcs from stopplay(), but funcs is defined (at least, in this example) as a global var. Depending on what the calling code is that calls stopplay(), this is not the same scope as the global scope that funcs is created in.
Update stopplay, changing this.funcs.count to funcs to see if that is alerted with the array you created above.
Also, are you sure your funcs Array has count? I would try using length instead.
function stopplay(){
alert(funcs.length);
for (var i = 0; i< funcs.length; i++){
....
}
}
EDIT:
You're not saving the return value of setTimeout, so you can't clear the timeout. Passing clearTimeout with a function doesn't clear the timeout.
You could do something like this:
var timers = new Array();
function createfunc(i) {
return function() {
timers.push( setTimeout(function(){
//my function
}, i*1500)) ;
};
}
function stopplay(){
for (var i = 0; i< timers.length; i++){
clearTimeout(timers[i]);
}
}

Related

Confused about function closures

Can someone please explain why does it console log out 10, 10, 10 instead of 9, 9, 9?
When it goes through for loop shouldn't it stop at 9?
var foo = [];
for (var i = 0; i < 10; i++) {
foo[i] = function() {
return i;
};
};
console.log(foo[0]());
console.log(foo[1]());
console.log(foo[2]());
Whenever any function which is using any variable from parent scope is executed, it gets that value of a variable which it is holding at the time of function execution. In your case i is already reached to 10 at the time of execution because of i++.
For getting expected result, you can add IIFE to it, which will hold the value of i in its scope.
var foo = [];
for (var i = 0; i < 10; i++) {
foo[i] = (function(i) {
return function() {
return i;
};
})(i);
};
console.log(foo[0]());
console.log(foo[1]());
console.log(foo[2]());
You can use this syntax which keeps the context
for (let i = 0; i < 10; i++) {
foo[i] = () => i;
};
console.log(foo[0]());
console.log(foo[1]());
console.log(foo[2]());
https://jsfiddle.net/bandpay/8zat3bnn/

Scope and accessing outside variables in anonymous functions?

I'm sure I'm doing something dumb here, but I'm not sure what. I'm adding anonymous functions to an array for later execution, and creating them in a for loop with variables that change each iteration.
I'm expecting the logged values to be:
https:server.net/a
https:server.net/b
but instead I'm getting:
https:server.net/b
https:server.net/b
It looks like maybe when I redefine the function it overwrites the last version of it, but I'm not sure why. I would think that each anonymous function would be different.
Why? What am I doing wrong here?
Here's some sample code:
f = [];
items = ['a', 'b'];
for(var i = 0; i < items.length; i++){
var itemID = items[i];
var itemAccessUrl = `https://server.net/${itemID}`;
var func = function(){
console.log(itemAccessUrl);
};
f.push(func);
}
console.log(f.length);
for(var j = 0; j < f.length; j++){
func();
}
This is because of the nature of var scoping. var is not block scoped, it is "hoisted", as if you declared it at the top of your function.
You could declare itemAccessUrl using let instead of var, which is block scoped, but it depends on what browser support you require. (Having said that, you're using templated strings, so you should be fine)
What's happening here? When you invoke your array's functions, they use the current value of itemAccessUrl, i.e. the last assigned string, with char 'b'. This because their invocation happens after the completion of first for-loop.
You can use a closure:
f = [];
items = ['a', 'b'];
for(var i = 0; i < items.length; i++){
var itemID = items[i];
var itemAccessUrl = `https://server.net/${itemID}`;
var func = (function(param) {
return function () {
console.log(param);
};
})(itemAccessUrl);
f.push(func);
}
console.log(f.length);
for(var j = 0; j < f.length; j++){
f[j]();
}
or bind your function to current param:
f = [];
items = ['a', 'b'];
for(var i = 0; i < items.length; i++){
var itemID = items[i];
var itemAccessUrl = `https://server.net/${itemID}`;
var func = (function (param) {
console.log(param);
}).bind(null, itemAccessUrl);
f.push(func);
}
console.log(f.length);
for(var j = 0; j < f.length; j++){
f[j]();
}
Furthermore you have to change the second loop, invoking f[j]();

Javascript array with callback function

I tried to have better understanding of JavaScript. Here is a piece of code that I read from JavaScript function closures.
var funcs = [];
// create a bunch of functions
for (var i = 0; i < 3; i++) {
funcs.push(function() {
console.log(i);
})
}
// call them
for (var j = 0; j < 3; j++) {
funcs[j]();
}
The array funcs has a push callback function. I don't why in the J loop, funcs[j]() will call this function to print the i in the console.
I have tried to understand this sequencey by adding some console messages:
var funcs = [];
console.log("start");
for (var i = 0; i < 3; i++) {
console.log("i:" + i);
funcs.push(function(){
console.log(i);
})
}
console.log("J loop");
for (var j=0; j<3; j++) {
console.log("j:" + j);
funcs[j]();
}
As expected, there is 3 for all three functions.
My question is: How does funcs[j]() calls the funcs.push(...) function?
I understant the funcs[j] is reference the j element of the funcs array. But why having parentheses () will call the push(...) function?
function() {console.log(i);} is an expression which evaluates to a value that is function that logs i.
funcs.push is a function that adds a value to an array.
Putting () after a function will call that function.
funcs.push(some_value) calls the push function and passes some_value as the value to put in the array.
funcs.push(function() {console.log(i);}) adds the function to the array.
The value of funcs[0] becomes that function.
Putting () after a function will call that function.
funcs[0]() calls the function that is the first value in the array.
First, the 'i' variable are global, and ending the loop, i=3
Then, the functions inside funcs, use the variable "i", then, all functions print "3" in console.
Maybe you wanted to do this:
var funcs = [];
console.log("start");
for (var i = 0; i < 3; i++) {
console.log("i:" + i);
funcs.push(function(i){
console.log(i);
})
}
console.log("J loop");
for (var j=0; j<3; j++) {
console.log("j:" + j);
funcs[j](j);
}

Run functions that are stored in an array

I am pushing some functions into an array but I am not sure how to run the functions as I loop through the array.
I am trying to get red yellow green to output to the console.
Here is a fiddle to my JavaScript: http://jsfiddle.net/rYNv4/
Here is the code that I have so far as well:
var myArr = [];
myArr.push( logItRunner('red') );
myArr.push( logItRunner('yellow') );
myArr.push( logItRunner('green') );
console.log(myArr);
//Function to be stored in array. Should return the logIt function.
function logItRunner( arg ) {
return function() {
logIt( arg );
};
}
//Function to log a string to the console
function logIt (color) {
console.log(color);
};
//Function to loop through array and fire functions within the array
function runIt () {
for (var i = 0, len = myArr.length; i < len; i++) {
//Fire Function within Array at position i and log the argument to the console.
myArr[i];
};
};
runIt();
myArr[i] is a function reference. You call the function it refers to just like you do any other time you have a function reference, with ():
In your loop, change:
myArr[i];
to
myArr[i]();
See how I'm executing the myArri; below.
You've almost got it, you just forgot the ().
//Function to loop through array and fire functions within the array
function runIt () {
for (var i = 0, len = myArr.length; i < len; i++) {
//Fire Function within Array at position i and log the argument to the console.
myArr[i]();
};
};
It is all about how JavaScript closures work, this usually is the way we teach JavaScript closures, so your sample is a good one, since it clarifies the concept.
This is the way I usually do the exact same thing to teach JavaScript closures, which is completely similar to yours with some little differences:
var myArr = [];
myArr.push('red');
myArr.push('yellow');
myArr.push('green');
var funcArr=[];
function pushIt () {
for (var i = 0, len = myArr.length; i < len; i++) {
var func = (function(color){
return (function(){
console.log(color);
});
})(myArr[i]);
funcArr.push(func);
};
};
function runIt () {
for (var i = 0, len = funcArr.length; i < len; i++) {
funcArr[i]();
};
};
pushIt();
runIt();

Trying to create objects within a for loop

I'm sure there's a really simple solution to this but I can't wrap my head around it. I'm trying to create and array of objects within a for loop like so:
for(var i = 0; i < 100; i++) {
foos[i] = new Foo(i*10);
bars[i] = someObject.createBar({
x : 0,
y : 0,
foobar = function() {
foo[i].a = true;
}
});
}
When trying to run this I get 'cannot set property a of undefined', both foos and bars are declared earlier in the code as globals.
It works fine if I create foos as foos[0] and access through bars[0]. I suspect it's something to do with function level scoping but as far as i can see the arrays should be accessible on the global object....
You need to "anchor" the value of i. To do this...
for(var i=0; i<100; i++) {
(function(i) {
// now do stuff with i
})(i);
}
Try this:
for(var i = 0; i < 100; i++) {
foos.push( new Foo(i*10) );
bars.push( someObject.createBar({
x : 0,
y : 0,
foobar = function() {
foo[i].a = true;
}
}) );
}
You cannot set value "a" of undefined, because "foo[i]" is undefined. You never defined foo[i]. Did you mean foos[i], maybe?
Also, as others have said, your function foobar is going to use the same value of i for every object you create. You should create a new closure with i, which will allow you to define a local variable i that can be different for each interior function, as so:
for(var i=0; i<100; i++) {
(function(i) {
// now do stuff with i
})(i);
}
The value of i in the execution of foobar is the one at the end of your loop (100). Because the loop block is a closure.
You'd better store the value you want. For example :
for(var i = 0; i < 100; i++) {
foos[i] = new Foo(i*10);
bars[i] = someObject.createBar({
x : 0,
y : 0,
i : i,
foobar: function() {
foos[i].a = true;
}
});
}
Or use a intermediate closure in your loop to enclose i :
for(var i = 0; i < 100; i++) {
(function(i){
foos[i] = new Foo(i*10);
bars[i] = someObject.createBar({
x : 0,
y : 0,
foobar: function() {
foos[i].a = true;
}
});
})(i);
}
The first solution is more explicit, the second one is maybe now more usual.

Categories