6

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

4 回答 4

13

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.

于 2012-04-18T10:37:33.737 回答
3

您的问题是您i稍后在setTimeout()函数触发时引用变量,到那时,值i已经改变(它已经到for循环的末尾。为了使每个 setTimeout 保持适当的值i,您必须捕获该值i分别为每个setTimeout()回调。

上一个使用工厂函数的答案很好,但我发现自执行函数比工厂函数更容易键入和遵循,但两者都可以工作,因为两者都可以在闭包中捕获你想要的变量,这样你就可以引用它们的静态值在 setTimeout 回调中。

以下是自执行函数如何解决此问题:

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

要设置与 的值成比例的超时延迟i,您可以这样做:

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

自执行函数被传递了值,i并且包含该值的函数内部的参数被命名index,因此您可以参考index以使用适当的值。


在 ES6 中使用 let

使用 Javascript 的 ES6(2015 年发布),您可以letfor循环中使用它,它将为循环的每次迭代创建一个新的、单独的变量for。这是解决此类问题的一种更“现代”的方法:

const end = 8;
for (let i = 1; i < end; i++) {            // use "let" in this line
     setTimeout(function() {
         console.log(i);
     }, 800);
 }
于 2012-04-18T10:59:25.963 回答
0

这不起作用的主要原因是,setTimeout其中设置为800i.

到它执行的时候,它的值i已经改变了。因此无法收到明确的结果。就像 TJ 说的那样,解决这个问题的方法是通过处理函数。

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

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

演示

于 2012-04-18T10:46:31.410 回答
-2

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);
}
于 2012-04-18T10:33:22.420 回答