Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to pass variable to anonymous function

Tags:

javascript

I want to pass variable setTimeoutfunction and do something with that. When I alert value of i it shows me numbers that i did not expected. What i m doing wrong? I want log values from 1 till 8.

var end=8;
for (var i = 1; i < end; i ++) {
       setTimeout(function (i) {
           console.log(i);   

       }, 800);
   }
like image 558
alexeyb Avatar asked Apr 18 '12 10:04

alexeyb


People also ask

How do you assign an anonymous function to a variable?

We can write an anonymous function using the function keyword, followed by parentheses (). We can write the function statements, just like we do for any other javascript function inside the curly parentheses {}. We store the results returned by an anonymous function in variables.

How do you pass an argument to anonymous function?

The syntax is simple: you can simply declare the anonymous function and make it execute by just calling it using the parenthesis at the end of the function. You can simply pass the parameters inside the immediate execution of the anonymous function as we have seen in the above example.

Is it valid to pass an anonymous function?

Anonymous functions can be used as an argument to other functions or as an immediately invoked function execution.

Can you assign a anonymous function to a variable true false?

Yes! An anonymous function can be assigned to a variable.


2 Answers

The standard way to solve this is to use a factory function:

var end=8;
for (var i = 1; i < end; i ++) {
       setTimeout(makeResponder(i), 800);
   }

function makeResponder(index) {
    return function () {
        console.log(index);   
   };
}

Live example | source

There, we call makeResponder in the loop, and it returns a function that closes over the argument passed into it (index) rather than the i variable. (Which is important. If you just removed the i argument from your anonymous function, your code would partially work, but all of the functions would see the value of i as of when they ran, not when they were initially scheduled; in your example, they'd all see 8.)


Update From your comments below:

...will it be correct if i call it in that way setTimeout(makeResponder(i),i*800);?

Yes, if your goal is to have each call occur roughly 800ms later than the last one, that will work:

Live example | source

I tried setTimeout(makeResponder(i),setInterval(i));function setInterval(index) { console.log(index*800); return index*800; } but it's not work properly

You don't use setInterval that way, and probably don't want to use it for this at all.


Further update: You've said below:

I need first iteration print 8 delay 8 sec, second iteration print 7 delay 7 sec ........print 2 delay 2 sec ...print 0 delay 0 sec.

You just apply the principles above again, using a second timeout:

var end=8;
for (var i = 1; i < end; i ++) {
       setTimeout(makeResponder(i), i * 800);
   }

function makeResponder(index) {
    return function () {
        var thisStart = new Date();
        console.log("index = " + index + ", first function triggered");
        setTimeout(function() {
            console.log("index = " +
                        index +
                        ", second function triggered after a further " +
                        (new Date() - thisStart) +
                        "ms delay");
        }, index * 1000);
   };
}

Live example | source

I think you now have all the tools you need to take this forward.

like image 122
T.J. Crowder Avatar answered Sep 18 '22 13:09

T.J. Crowder


Your problem is that you are referring to the variable i some time later when your setTimeout() function fires and by then, the value of i has changed (it's gone to the end of the for loop. To keep each setTimeout with it's appropriate value of i, you have to capture that value i separately for each setTimeout() callback.

The previous answer using a factory function does that just fine, but I find self executing functions a little easier than factory functions to type and follow, but both can work because both capture the variables you want in a closure so you can reference their static value in the setTimeout callback.

Here's how a self executing function would work to solve this problem:

var end=8;
for (var i = 1; i < end; i ++) {
       (function (index) {
           setTimeout(function() {
               console.log(index);
           }, 800);
       })(i);
   }

To set the timeout delay in proportion to the value of i, you would do this:

var end=8;
for (var i = 1; i < end; i ++) {
    (function (index) {
        setTimeout(function() {
            console.log(index);
        }, index * 800);
    })(i);
}

The self executing function is passed the value of i and the argument inside that function that contains that value is named index so you can refer to index to use the appropriate value.


Using let in ES6

With the ES6 of Javascript (released in 2015), you can use let in your for loop and it will create a new, separate variable for each iteration of the for loop. This is a more "modern" way to solve a problem like this:

const end = 8;
for (let i = 1; i < end; i++) {            // use "let" in this line
     setTimeout(function() {
         console.log(i);
     }, 800);
 }
like image 30
jfriend00 Avatar answered Sep 19 '22 13:09

jfriend00