【问题标题】:setTimeout inside for loop [duplicate]for循环内的setTimeout [重复]
【发布时间】:2010-12-19 02:25:12
【问题描述】:

我希望一个字符串逐个字符地出现,代码如下:

function initText()
{
    var textScroller = document.getElementById('textScroller');
    var text = 'Hello how are you?';

    for(c = 0; c < text.length; c++)
    {
        setTimeout('textScroller.innerHTML += text[c]', 1000);
    }
}

window.onload = initText;

它不起作用..我做错了什么?

【问题讨论】:

    标签: javascript loops settimeout


    【解决方案1】:

    试试这个:

    function initText()
    {
        var textScroller = document.getElementById('textScroller');
        var text = 'Hello how are you?';
    
    for(c = 0; c < text.length; c++)
    {
        setTimeout("textScroller.innerHTML += '" + text[c] + "'", 1000 + c*200);
    }
    }
    
    window.onload = initText;
    

    【讨论】:

    • 已编辑以删除测试变量并将 text[c] 作为字符串附加到单引号中。
    • 如果文本包含 '、`` 或换行符,则会失败。最好避免像 timeout-with-string-argument 这样在字符串中创建和执行代码。
    【解决方案2】:

    您的 for 循环会同时为每个字符设置超时,因此它们不会按顺序出现,而是同时出现。您的 setTimeout 应包含另一个 setTimeout 的代码,该代码将包含要显示的下一个字符。

    类似这样的东西(没有测试过)

    function initText()
    {
        var textScroller = document.getElementById('textScroller');
        var text = 'Hello how are you?';    
        setTimeout('nextChar(text)', 1000);
    }
    function nextChar(text){
        if(text.length > 0){
            textScroller.innerHTML += text[0]; 
            setTimeout('nextChar(text.substring(1))', 1000);
        }
    }
    

    【讨论】:

      【解决方案3】:

      试试这样的:

      function initText()
      {
          var textScroller = document.getElementById('textScroller');
          var text = 'Hello how are you?';
      
          var c = 0;
          var interval = setInterval(function() { 
                                textScroller.innerHTML += text[c]; 
                                c++; 
                                if(c >= text.length) clearInterval(interval);
                         }, 1000);
      
      }
      

      请注意,我添加了clearInterval 以在需要时停止它。

      【讨论】:

      • 你说得对,setInterval 是一个更好的主意,没想到
      • 我明白了,谢谢!在这种情况下,没有定义(标准)循环。那么在这种情况下,函数充当循环?这对我来说很新鲜但很有趣:)
      • 循环播放的是setInterval。就像你正在做的那样循环 setTimeout :)
      • 如果设置innerHTML出错会怎样?我会告诉你:另一个错误,另一个,另一个,等等。setInterval 很危险。反复使用setTimeout 更安全。
      • 是的,很高兴了解 setInterval 和循环,
      【解决方案4】:

      尝试使用闭包:

      function init() {
          var textScroller = document.getElementById('textScroller');
          var text = 'Hello how are you?';
          var c = 0;
          function run() {
              textScroller.innerHTML += text[c++];
              if (c<text.length)
                  setTimeout(run, 1000);
          }
          setTimeout(run, 1000);
      }
      init()
      

      您的代码中的问题是您放入字符串中的代码将在全局上下文中运行,其中未定义 textScroller(它在您的函数内部定义)。

      【讨论】:

        【解决方案5】:

        目前,您定义了 18 个超时,所有超时都将立即执行 ~。 第二个问题是,您传递指令以作为字符串执行。在这种情况下,代码将无法访问 initText 中定义的所有变量,因为评估代码将在全局范围内执行。

        IMO,这应该可以完成工作

        function initText(){
            var textScroller = document.getElementById('textScroller');
            var text = 'Hello how are you?';
        
            var c = 0;
        
            (function(){
                textScroller.innerHTML += text.charAt(c++);
                if(text.length > c){
                    setTimeout(arguments.callee, 1000);
                }
            })();
        }
        

        【讨论】:

          【解决方案6】:

          我想分享一个 sn-p(基于 Soufiane Hassou 的回答)。它扩展到当您从字面上替换 for 循环体以在固定时间间隔内迭代某个数组时的情况。基本相同的同步循环,但有“睡眠”暂停(因为 javascript 不是同步编程语言)。

          function loop(arr, take, period) {
              period = period || 1000;
              var i = 0;
              var interval = setInterval(function() { 
                  take(i, arr[i]);
                  if (++i >= arr.length) { clearInterval(interval);}
              }, period);
          }
          

          使用示例:

          loop([1, 2, 3, 4], function(index, elem){
              console.log('arr[' + index + ']: ' + elem);
          });
          

          在 Node JS 中测试。希望对某人有所帮助。

          编辑>

          以下更新使代码可以与进行大量“原型设计”(如 jQuery 或原型)的库一起使用:

          function loop(arr, take, period) {
              period = period || 1000;
              var scope = {
                  i: 0,
                  arr: arr,
                  take: take,
              };
              var iterate = (function iterate() {
                  if (this.i >= this.arr.length) { clearInterval(this.interval); return}
                  take(this.i, this.arr[this.i++]);
              }).bind(scope);
              scope.interval = setInterval(iterate, period);
          }
          

          【讨论】:

            【解决方案7】:

            如果您想保留 setTimeOut(而不是 setInterval)并使用命名函数(而不是在 setTimeOut 调用中评估代码块),那么这可能会有所帮助:

            var b = {
              textScroller: document.getElementById('textScroller'),
              text: "Hello how are you?"
            };
            
            
            function initText() {
              for(c = 0; c < b.text.length; c++) {
                setTimeout("append("+c+")", 1000 + c*200);
              }
            }
            
            function append(c) {
              b.textScroller.innerHTML += b.text[c];
            }
            
            window.onload = initText;
            

            通过以上方式,您可以将参数传递给附加函数。

            要传递几个参数,接下来的代码就可以了:

            var glo = [];
            
            function initText()
            {
              var textScroller = document.getElementById('textScroller');
              var text = "Hello how are you?";
              var timeout_time;
              for(c = 0; c < text.length; c++) {
                glo[glo.length] = {text:text, c:c, textScroller:textScroller};
                timeout_time = 1000 + c * 200;
                setTimeout("append(" + (glo.length - 1) + ")", timeout_time);
              }
            }
            
            function append(i)
            {
              var obj = glo[i];
              obj.textScroller.innerHTML += obj.text[obj.c];
              obj = null;
              glo[i] = null;
            }
            
            window.onload = initText;
            

            有了上面你只有一个全局数组glo。在循环中,您为glo 创建新的数组成员,并在append() 函数中使用作为参数传递的索引来引用这些成员。

            注意:第二个代码示例并不是解决 OP:s 问题的最佳或最合适的解决方案,但可能会在其他 setTimeOut 相关问题中受益,例如。当有人想要进行演示或性能测试时,需要在一些延迟后调用某些功能。这段代码的优点是利用了 for 循环(许多编码人员希望使用 for 循环)以及使用内部循环的可能性以及将循环时间状态下的局部变量“发送”到 timeOut 函数的能力。

            【讨论】:

              【解决方案8】:

              比@yauhen-yakimovich 的answer 更通用:

              使用Timeout:

              var repeat = (function () {
                  return function repeat(cbWhileNotTrue, period) {
                      /// <summary>Continuously repeats callback after a period has passed, until the callback triggers a stop by returning true.  Note each repetition only fires after the callback has completed.  Identifier returned is an object, prematurely stop like `timer = repeat(...); clearTimeout(timer.t);`</summary>
              
                      var timer = {}, fn = function () {
                          if (true === cbWhileNotTrue()) {
                              return clearTimeout(timer.t); // no more repeat
                          }
                          timer.t = setTimeout(fn, period || 1000);
                      };
                      fn(); // engage
                      return timer; // and expose stopper object
                  };
              })();
              

              使用Interval:

              var loop = (function () {
                  return function loop(cbWhileNotTrue, period) {
                      /// <summary>Continuously performs a callback once every period, until the callback triggers a stop by returning true.  Note that regardless of how long the callback takes, it will be triggered once per period.</summary>
              
                      var timer = setInterval(function () {
                          if (true === cbWhileNotTrue()) clearInterval(timer);
                      }, period || 1000);
                      return timer; // expose stopper
                  };
              })();
              

              在 cmets 中表示的两者之间的细微差别 -- repeat 方法仅在回调执行后重复,因此如果您有一个“慢”回调,它不会每 delay 毫秒运行一次,但 重复 在执行之间的每个delay 之后,而loop 方法将每delay 毫秒触发一次回调。为了提前停止,repeat 使用一个对象作为返回的标识符,因此请改用clearTimeout(timer.t)

              用法:

              就像@soufiane-hassou 的answer

              var textScroller = document.getElementById('textScroller');
              var text = 'Hello how are you?';
              
              var c = 0;
              var interval = repeat/* or loop */(function() { 
                                    textScroller.innerHTML += text[c]; 
                                    c++; 
                                    return (c >= text.length);
                             }, 1000);
              

              如前所述,过早停止是:

              /* if repeat */ clearTimeout(interval.t);
              /* if loop */   clearInterval(interval);
              

              【讨论】:

                【解决方案9】:

                级联循环可能会更好。以淡化 div 为例:

                div=document.createElement('div');
                div.style.opacity=1;
                setTimeout(function(){fade(1);},3000);
                function fade(op){
                    op-=.05;
                    if(op>0) setTimeout(function(){div.style.opacity=op;fade(op);},30);
                    else document.body.removeChild(div);
                }
                

                【讨论】:

                  猜你喜欢
                  • 2016-08-30
                  • 2014-05-18
                  • 1970-01-01
                  • 2019-10-21
                  • 2018-04-26
                  • 1970-01-01
                  • 1970-01-01
                  • 1970-01-01
                  相关资源
                  最近更新 更多