【问题标题】:Javascript how to use setTimeout on an iterative list operation?Javascript如何在迭代列表操作上使用setTimeout?
【发布时间】:2011-11-12 23:27:05
【问题描述】:

我想做这样的事情:

for(var i=0;i<aList.length;i++)
{
    aList[i].doSomething();
    sleep(500);
}

当然,javascript中没有睡眠功能,所以我尝试了以下方法:

for(var i=0;i<aList.length;i++)
{
    setTimeout(function(){
        aList[i].doSomething();
    },500);  
}

但是,现在它说 aList[i] 没有定义。由于匿名函数是一个闭包,它实际上是从外部函数的范围内读取 aList[i],因此当 setTimeout 中的函数运行时,i 已经发生了变化。

有什么方法可以做到这一点?

【问题讨论】:

    标签: javascript closures settimeout anonymous-function


    【解决方案1】:

    模拟 JavaScript 1.7 的 let 的快速修复方法是将其包装在一个函数中:

    for(var i=0; i < aList.length; i++) {
        (function(i) {
            setTimeout(function() {
                aList[i].doSomething();
            }, 500 * i); // <-- You need to multiply by i here.
        })(i);
    }
    

    我还修复了一个小错误,其中脚本将暂停 500 秒,然后执行所有这些。 setTimeout 是非阻塞的。

    【讨论】:

      【解决方案2】:

      这可能是你想要的:

      for(var i=0;i<aList.length;i++)
      {
          (function(i){
              // Retain `i` in this scope, for use later (after timeout)
              setTimeout(function(){
                 aList[i].doSomething();
              }, 500 * i);
          })(i);
      }
      

      您希望500 * i 使每一步都比上一步晚 500 毫秒,否则一切都会在 500 毫秒后立即发生。请注意额外的包装函数——这实际上捕获/保留了i 的值。

      【讨论】:

      • @minitech,谢谢,但仅供参考,它没有任何区别。 AFAIK,这是一个偏好问题。
      【解决方案3】:

      其中一种变体:

      var array = [/*elements*/],
          i,
          length = array.length,
          loop = setInterval(function() {
              array[i].doSomething();
              i += 1;
              if (i === length) {
                  clearInterval(loop);
              }
          }, 500);
      

      【讨论】:

        【解决方案4】:

        只要定义超时前的函数

        for(var i=0; nextFunction = aList[i];i++) {
            var waitAndDoSomething = function(func) {
                return function() { func.doSomething(); }
            };
            setTimeout(waitAndDoSomething(nextFunction),500*i);  
        }
        

        另一个可能的答案

        在 minitect 的建议下,我开始考虑其他可能的解决方案。我觉得干净的解决方案是使用bind

        String.prototype.doSomething = function() { alert(this); } // Just for testing
        var aList = ['one', 'two', 'three']; // Just for testing
        
        for(var i=0;obj = aList[i];i++) { 
            setTimeout(obj.doSomething.bind(obj),500*i); 
        }
        

        但是,我不确定这在 Razor Storm 问题的上下文中效果如何,因为我不知道 aList[i] 代表什么。

        这能回答问题吗

        我也想知道这是否是预期的行为。它实际上不会在执行后休眠,而是设置时间。这可能是骗人的,但是如果我们真的想执行一个函数,然后在下一个函数之前休眠半秒,我们的时间就在这里了。相反,我会使用递归:

        String.prototype.doSomething = function() { alert(this); }
        var aList = ['one', 'two', 'three'];
        
        var doNextSomething = function(index) {
            if (!index) { index = 0 }
            if (nextObject = aList[index]) {
                nextObject.doSomething();
                setTimeout(doNextSomething, 500, index + 1);    
            }
        };
        
        doNextSomething();
        

        现在,它会在函数执行后等待 500 毫秒(在长时间运行的任务中很明显)。

        我认为发布的答案更符合 Razor Storm 的想法。

        【讨论】:

        • 这实际上也行不通,因为 JavaScript 除了函数作用域之外没有作用域。 nextFunction 将被重新定义。 jsfiddle.net/N9LBU 另外,您没有解决 * i 问题。 -1
        • 好吧,你仍然可以在你的帖子中更正它 - 投票是灵活的。
        【解决方案5】:

        在 for 循环之前执行以下操作:

        var _aList = aList;
        

        在循环中你必须设置超时和延迟 500 * i。

        所以结果会是:

        var _aList = aList;
        for(var i=0;i<aList.length;i++)
        {
            (function(obj, index) {
                setTimeout(function(){
                   obj.doSomething();
                },500 * index); 
            })(aList[i], i);
        }
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2023-03-17
          • 2019-07-26
          • 2015-11-10
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-08-25
          • 2013-06-29
          相关资源
          最近更新 更多