use apply() to execute array of setTimeouts with args - javascript

I need to be able to build an array of anonymous functions that execute setTimeout with unique arguments per function. The following code has already been proposed, and works, on SO, but it only deals with immediate execution of the setTimeout. It does not explain how one can accumulate a collection of setTimeout functions to an array, with each setTimeout function having its own closure that defines a unique variable to be used in the execution of the setTimeout.
This is what exists already on SO:
var strings = [ "hello", "world" ];
for(var i=0;i<strings.length;i++) {
setTimeout(
(function(s) {
return function() {
alert(s);
}
})(strings[i]), 1000);
}
This is one of many permutations of what I have tried:
var strings = [ "hello", "world" ];
var func_array = [];
for (var i=0; i < strings.length; i++) {
func_array.push(function() {
setTimeout(
(function(s) {
return function() {
alert(s);
}
})(strings[i]), 1000);
});
}
When apply() is called on func_array, the strings[i] value is the same for each call, i.e. the final member of the strings array.
Another attempt: here we make the function that gets pushed onto func_array self-executing in order to scope the strings[i] variable correctly. However, apply() sees only 'undefined' since the function has already executed.
var strings = [ "hello", "world" ];
var func_array = [];
for (var i=0; i < strings.length; i++) {
func_array.push(function(s) {
setTimeout(
(function(s) {
return function() {
alert(s);
}
})(s), 1000);
}(strings[i]));
}
Ideas?

Use Function.prototype.bind(). It becomes much simpler than it actually is.
var strings = [ "hello", "world" ];
var func_array = [];
for (var i=0; i < strings.length; i++) {
var fn = function(string){
return setTimeout(function(){ alert(string) } ,1000);
};
func_array.push(fn.bind(this, strings[i]));
}

This does what you want.
var strings = ["hello", "world"];
var func_array = [];
for (var i = 0; i < strings.length; i++) {
(function (s) {
var func = function () {
alert("String is " + s);
}
var futureFunc = function () {
setTimeout(func, 1000);
}
func_array.push(futureFunc);
} (strings[i]));
}
func_array[0]()
func_array[1]()

Related

in JS I Don't Know this Expression

function makeArmy() {
let shooters = [];
for (let i = 0; i < 10; i++) {
let shooter = function() {
alert(i);
};
shooters.push(shooter);
}
return shooters;
}
let army = makeArmy();
army[0](); < -- - what is this ?
army[5](); < -- - what is this ?
Let's do this back-to-front:
let army = makeArmy();
That calls a function...
// ...and the function
function makeArmy() {
// Creates an array
let shooters = [];
// It loops from 0 to 9 pushing a new function
// into the array on each iteration
for (let i = 0; i < 10; i++) {
// Create the new function
let shooter = function() {
// I used console.log because it's much less hassle :)
console.log(i);
};
// Add the function to the array
shooters.push(shooter);
}
// And then you return the array of functions
return shooters;
}
// An array of functions!
let army = makeArmy();
// Call the first function in the array
army[0]();
// Call the fourth function in the array
army[3]();

Stop passing by reference in closures [duplicate]

This question already has answers here:
How do JavaScript closures work?
(86 answers)
Closed 7 years ago.
I have code that looks like this:
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = function() {
console.log(i);
}
}
Unfortunately, it seems that i is being passed by reference, so all the functions in a output 10. How do I make it so that each function outputs the value that i had when it was created? I.e. a[0]() gives 0, a[1]() gives 1, etc.
EDIT: to clarify, I do not want a to store the values 0-9. I want a to store functions that return the values 0-9.
You need to invoke a function (to create a closure that captures your value) which returns a function (the one you want to end up with). Something like this:
var a = [];
for (var i = 0; i < 10; i++) {
a[i] = (function(value) {
return function() {
console.log(value);
}
})(i);
}
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = (function(j) {
return function () {
console.log(j);
}
})(i);
}
A better performing version -
function makeFunction(i) {
return function () {
console.log(i);
}
}
var a = [];
for(var i = 0; i < 10; i++) {
a[i] = makeFunction(i);
}
JSFiddle Demo.

Javascript Function not executing/returning value

I have a function and I want to see what the value of index is. It gives me 0 however. I thought that was wierd so I put a console.log in function() to see if it was executing or not and I didn't recieve an output which tells me that function() isn't getting called. Not sure what I'm doing wrong.
function jsTest() {
var index = 0;
var counter = 0;
var obj = {};
obj.index = index; //obj.index = 0 at this point
var func = function () {
for (index = 0; index < 10; index++) {
counter += 2;
console.log(counter); //Doesn't execute for some reason
}
obj.index++;
};
obj.func = func; //executes function()
this.index++;
return index;
}
var x = jsTest();
console.log(x);
obj.func = func;
doesn't actually call func, it assigns the property func for obj to be the function func. If you want to call func, you should add parentheses afterwards, like
obj.func = func();

Javascript - Variable updating

This is probably an easy question but it's late at night and I can't get my head round this.
here's my code
$(document).ready(function () {
var items = getNumber();
for (var i = 0; i < items.length; i++) {
var test = items[i].action;
test();
}
});
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({ id: i, number: num, action: function () { alert(i) } });
}
return items
}
Could someone explain to me why the alert output is always 5? I want the alert parameter to be the index at the time it is added to the array. It seems like it is being dynamic.
If you could also post a solution how i could get this to work i would be extremely thankful
This is a common issue with JavaScript variable scoping: new variables are only introduced in a new execution context and thus, in the problematic code, i is shared across all the action callbacks.
Anyway, here is the corrected code following the standard idiom:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
// _i is a new variable for each callback
action: (function (_i) {
// use separate (one per callback) _i variable
return function () { alert(_i) }
})(i) // pass in current value for loop
});
}
return items
}
Alternatively, if one doesn't like all the nesting, it's fine to use a "named" function to perform the same task. The key to point is that the closure is created (and returned from) a new execution context so that a different variable is closed-over:
function getNumber()
{
function mkAction (i) {
return function () { alert(i) }
}
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: mkAction(i)
});
}
return items
}
Another approach is to use Function.bind from ES5:
function getNumber()
{
var items = [];
for (var i = 0; i < 5; i++) {
var num = i * 10;
items.push({
id: i, number: num,
action: (function (_i) { alert(_i) }).bind(null, i)
});
}
return items
}
(Note that Function.bind can be emulated using a new execution context/closure even without native browser support. See the MDC documentation.)
See also:
JavaScript closure inside loops – simple practical example
Passing functions to setTimeout in a loop: always the last value?
How do JavaScript closures work?

Javascript giving parameters to inner function

So my basic setup is this:
for (var i = 0; i < 3; i++) {
var indices = [-1, -1, -1];
while (index == -1) {
// Do Stuff
index[i] = newIndex;
}
var press = function() { alert(i); };
new control({press: press});
}
Now when I press the each of the new controls instead of getting alert(0), alert(1) and alert(2) I get alert(3), alert(3) and alert(3).
I can kind of understand whats going on. Now my question: how can i pass the different indexes to the function as I intended?
It is because closure variable i, the solution is to create a private closure for each loop.
for (var i = 0; i < 3; i++) {
var indices = [-1, -1, -1];
while (index == -1) {
// Do Stuff
index[i] = newIndex;
}
var press = (function(myvar){
return function() { alert(myvar); };
})(i);
new control({press: press});
}
Use closure:
var press = (function (x) {
return function () {
alert(x);
};
})(i);
This way the current i value is saved in a safe place, a private function.
Note that declaring variables (with var) inside loops are not standard, you should declare the press variable outside the loop.

Categories

Resources