setTimeout()的问题

时间:2011-07-31 17:27:56

标签: javascript

这是我的代码。我想要它做的是写0,等待一秒,写1,等待一秒,写2,等待一秒等等。相反它写5 5 5 5 5

for(i = 0; i < 5; i++) {
    setTimeout("document.write(i + ' ')", 1000);
}

http://jsfiddle.net/Xb7Eb/

8 个答案:

答案 0 :(得分:8)

1)您将所有超时设置为同时持续1秒。循环不等待超时发生。所以你有5次超时都同时执行。

2)当超时执行时,循环很长,完成后i变为5.所以一旦执行,它们都会打印“5”

3)document.write()将somthing写入页面,在它执行的相同位置。即如果你在一段文字的中间有<script>document.write("xyz")</script>,它会在文本的中间写上“xyz”。但是,超时不一定在页面上的任何位置。它们只存在于代码中。

这是一个尽可能接近你的解决方案:http://jsfiddle.net/rvbtU/1/

var container = document.getElementById("counter");
for(i = 0; i < 5; i++) {
    setTimeout("container.innerHTML += '" + i + " ';", 1000 * i);
}

但是,该解决方案使用setTimeout将字符串计算为javascript的能力,这绝不是一个好主意。

以下是使用anymous函数的解决方案:http://jsfiddle.net/YbPVX/1/

var container = document.getElementById("counter");
var writer = function(number) {
    return function() { container.innerHTML += String(number) + " "; };
}
for(i = 0; i < 5; i++) {
    setTimeout(writer(i), 1000 * i);
}

编辑:忘了保存第二小提琴。哎呦。现在修好了。

答案 1 :(得分:3)

大多数可用的答案都提供了不好的建议。 *具体来说,你不应该再将字符串传递给setTimeout(它仍然有效,但不鼓励),它不是更长2000,有更好的方法可以做到这一点。

setTimeout将函数作为第一个参数,这就是你应该做的,但是在循环中调用setTimeout时会出现一些问题。

这看起来应该有效:

var i;
for ( i = 0; i < 5; i++ )
{
  setTimeout(function(){
    document.write( i + ' ' );
  }, 1000 * (i + 1) );
}

但事实并非如此。问题是,当setTimeout执行该函数时,循环将i增加到5,因此您将重复相同的值。

有一些修复。如果您愿意冒with声明,可以尝试以下方法:

var i;
for ( i = 0; i < 5; i++ )
{
  with( { i:i } )
  {
    setTimeout(function(){
      document.write( i + ' ' );
    }, 1000 * (i+1) );
  }
}

请注意,通常不建议使用with,就像将string值传递给setTimeout一样,所以我并不建议这种做法。

更好的方法是使用闭包:

var i;
for ( i = 0; i < 5; i++ )
{
  (function(i){
    setTimeout(function(){
      document.write( i + ' ' );
    }, 1000 * (i+1) );
  })(i);
}

为了解释发生了什么,匿名函数包装器(function(i){...code...})立即执行,因为它包含在parens中并作为值传递i

(function(i){...code...})(i);

这会强制i使用的document.write变量与for循环中使用的变量不同。如果差异太混乱,您甚至可以更改匿名函数包装器中使用的参数:

(function(a){document.write(a+' ')})(i);

*当我开始撰写此问题时,有许多答案描述了如何修复字符串以使用setTimeout,尽管他们技术上工作,他们没有'包括为什么它们可以工作(因为'document.write(“'+ i +'”);'由于字符串连接而在调用时评估i,而不是评估{{ 1}}在运行时像以前的版本一样),他们肯定没有提到它是调用i的旧方法。

答案 2 :(得分:2)

var i = 1;
function timeout(){

    document.write(i + ' ');
    i++;
    if (i == 5) return;
    setTimeout(timeout, 1000);
}
 timeout();

http://jsfiddle.net/nnJcG/1/

答案 3 :(得分:1)

您还可以使用setIntervalclearInterval

var i = 0;

var f = setInterval(function() {
    if(i == 4) clearInterval(f);
    document.write(++i + ' ');
}, 1000);

我认为这段代码非常易读。

答案 4 :(得分:1)

你有一个clousures的问题,你可以试试这个:

var timeout = function(){
    var i = 0;
    return function(){                
        document.write(i+ ' ');
            i++;
            if(i!==5)
            setTimeout(timeout,1000);
    };
}();
setTimeout(timeout,1000);

以下是jsBin http://jsbin.com/uloyuc/edit

中的示例

答案 5 :(得分:1)

首先,从不将字符串传递给setTimeout。使用功能,它更清洁。

其次,你必须“关闭”循环值。我打赌这就是你想要的。

for(var i = 0; i < 5; i++) {
  (function(i) {
    setTimeout(function() {
      document.write(i + ' ')
    }, i * 1000);
  }(i));
}

在此处查看有关关闭循环值的自执行函数的更多信息http://www.mennovanslooten.nl/blog/post/62


正因为我喜欢它,这里是CoffeeScript中的等价物,而do关键字可以帮助解决这个问题。

for i in [0..4]
  do (i) ->
    setTimeout ->
      document.write "#{ i } "
    , i * 1000

答案 6 :(得分:1)

你可以尝试这样:

var tick_limit = 5; // Or any number you wish representing the number of ticks
var counter = 0; // Or any number you wish 
var timer_interval = 1000; // Interval for the counter
var timer;

function timerTick()
{
 if(counter < tick_limit)
 {
   // Execute code and increase current count
   document.body.innerHTML+=(counter + ' '); // Append the counter value to the body of the HTML page
   counter++;
   timer = setTimeout(timerTick,timer_interval);
 }
 else
 {
  // Reset everything
  clearTimeout(timer);
  counter = 0;
 }
}

function startCounter()
{
 clearTimeout(timer); // Stop current timer
 timer = setTimeout(timerTick,timer_interval); // Start timer with any interval you wish
}
...
// Start timer when required
startCounter();
...

这样,多次调用startCounter将导致单个计时器执行代码

答案 7 :(得分:0)

你同时触发五次超时。

我喜欢Pindatjuh's answer,但这是另一种有趣的方式。

这种方式在前一个超时完成后开始下一个超时:

// Wrap everything in a self executing anonymous function so we don't pollute
//    the global namespace.
//
//    Note: Always use "var" statments or you will pollute the global namespace!
//    For example "for(i = 0; i < 5; i++)" will pollute the global namespace
//    unless you have "var i; for(i = 0; i < 5; i++)" or 
//    "for(var i = 0; i < 5; i++)" & all of that is not in the global namespace.
//    
(function() {

    // "i" will be available within doThis()
    //     you could also pass "i" as an argument
    var i = 0,
        doThis = function() {

            // setTimeout can take an anonymous function
            // or a regular function. This is better than
            // eval-ing a string.
            setTimeout(function() {
                document.write(i + ' ');
                ++i;

                // Do the function again if necessary
                if (i < 5) doThis();
            }, 1000);
        }

    // Let's begin! 
    doThis();

})();

Working Example