简体   繁体   中英

How to pass variable to anonymous function

I want to pass variable setTimeout function and do something with that. When I alert value of i it shows me numbers that i did not expected. What im 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);
   }

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; } setTimeout(makeResponder(i),setInterval(i));function setInterval(index) { console.log(index*800); return index*800; } 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.

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);
 }

The main reason for this to not to work, is because, of the setTimeout which is set to run after 800 and the scope of i .

By the time it executes which the value of i will already have changed. Thus no definitive result could be received. Just like TJ said, the way to work this around is through a handler function.

function handler( var1) {
    return function() {
      console.log(var1);  
    }        
}

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

Demo

setTimeout accepts variables as additional arguments:

setTimeout(function(a, b, c) {
    console.log(a, b, c);
  }, 1000, 'a', 'b', 'c');

Source .

EDIT: In your example, the effective value of i will likely be 8 , since the function is merely to be called after the loop has finished. You need to pass the current value of i for each call:

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM