【问题标题】:Looping through setTimeout function循环通过 setTimeout 函数
【发布时间】:2015-08-24 13:47:07
【问题描述】:

我正在尝试编写一个函数,它通过 x 步(本示例中的步骤 1、步骤 2)循环 x 次(本示例中为 3)x 秒(在本示例中,步骤 1 需要 2 秒,步骤 2需要 1 秒)。

所以我希望它循环如下:

第 1 步(2 秒) 步骤 2(1 秒) 第 1 步(2 秒) 步骤 2(1 秒) 第 1 步(2 秒) 第 2 步(1 秒)

我有下面的代码,但它只遍历循环一次,我不知道为什么。

jQuery('input').click(function () {
    for(i = 0; i < 3; i++){
        jQuery('div').html('Step 1');
        setTimeout(function() { 
            jQuery('div').html('Step 2');
        },2000);
        setTimeout(function() { 
            jQuery('div').empty();
        },2000 + 1000);
    }
});

我希望循环以相同的时间继续,但在到达.empty(); 后返回开始。

JSFiddle:http://jsfiddle.net/kmyjhgvd/

【问题讨论】:

  • @Pablo 是正确的,这是一个显示他的意思的小提琴,打开控制台并查看日志触发 jsfiddle.net/kmyjhgvd/2
  • 你为什么在这里使用 for 循环?如果您需要它是时间敏感的 AVOID 循环,请使用 RECURSION。递归方法可以根据布尔值/条件调用自身,然后重新开始。这将是更准确的计时器,(尽管即使在当今时代,计时器也会因循环和递归而失败)
  • @Pablo 没有充分的理由使用循环,因为它在运行时难以预测。一个循环可能会触发所有彼此非常接近的超时,或者在循环间隔之间滞后。在使用超时和间隔时,递归将为您提供更加统一的时间安排,请使用递归。
  • @Mike:你描述了一个循环不存在的问题,但递归确实存在。如果你使用递归,那么每次迭代的时间将取决于之前的所有,所以最后你积累了一个滞后。使用循环,所有超时都具有相同的起点,因此它们不会累积延迟。

标签: javascript jquery html for-loop settimeout


【解决方案1】:

循环迭代 3 次,但超时始终相同:2000 和 3000,因此您创建了 3 个相同的回调,在 2 秒内执行,另外 3 个在三秒内调用。在这里你有你想要的,有 6 种不同的超时:

http://jsfiddle.net/nsg7ny7L/1/

jQuery('input').click(function () {
    var time=0;
    for(i = 0; i < 3; i++){
        setTimeout(function() { 
            jQuery('div').html('Step 1');
        },time++*1000);
        setTimeout(function() { 
            jQuery('div').html('Step 2');
        },time++*1000);
    }
    setTimeout(function() { 
        jQuery('div').empty();
    },time++*1000);
});

PS:确保你理解它是如何工作的:JS是单线程的,所以for循环连续调用setTimeout 6次......所有它们的开始时间几乎相同

【讨论】:

    【解决方案2】:

    我没有尝试过代码,但我认为这应该可以工作

    jQuery('input').click(function () {
         loopAction (3, 1, 2); // x is number of loops - s1, s2 in seconds
    });
    
    function loopAction(x, s1, s2){
       If (x>0){                 // as long as x>0 start the iteration
          jQuery('div').html('Step 1');
          setTimeout(function() { 
              jQuery('div').html('Step 2');
              setTimeout(function() {
                 loopAction(x-1, s1, s2); // recursive call to self
              }, s2*1000);
          },s1*1000);
      } else {
         jQuery('div').empty();    // x>0 is false
      }
    }
    

    【讨论】:

    • 谢谢。据我所知,循环不适合及时控制动作,正如问题中的 cmets 所示。
    【解决方案3】:

    现在您在循环中直接执行了第 1 步,没有超时,因此所有三个第 1 步的发生都会立即发生。然后你有第 2 步超时,但所有这三个都有相同的时间延迟,所以它们都会在两秒后同时发生。你会在不同的时间得到这个:

       0: Step 1 (i = 0)
       0: Step 1 (i = 1)
       0: Step 1 (i = 2)
    2000: Step 2 (i = 0)
    2000: Step 2 (i = 1)
    2000: Step 2 (i = 2)
    3000: empty (i = 0)
    3000: empty (i = 1)
    3000: empty (i = 2)
    

    你会想要这个:

       0: Step 1 (i = 0)
    2000: Step 2 (i = 0)
    3000: Step 1 (i = 1)
    5000: Step 2 (i = 1)
    6000: Step 1 (i = 2)
    8000: Step 2 (i = 2)
    9000: empty
    

    将第 1 步和第 2 步设置为超时,并设置时间延迟,使它们相继发生,即使用i * 3000 作为偏移量。然后您可以在最后一步之后在单独的超时中清空元素:

    jQuery('input').click(function () {
      for(i = 0; i < 3; i++){
        setTimeout(function() { 
          jQuery('div').html('Step 1');
        }, i * 3000);
        setTimeout(function() { 
          jQuery('div').html('Step 2');
        }, i * 3000 + 2000);
      }
      setTimeout(function() { 
        jQuery('div').empty();
      }, 6000 + 3000);
    });
    

    【讨论】:

      【解决方案4】:
      jQuery('input').click(function () {
      
          var times = 0,
              printStep1 = function(){
                  JQuery('div').html('Step 1');
                  setTimeout( printStep2, 1000 );
              },
              printStep2 = function(){
                  JQuery('div').html('Step 2');
                  if( times++ < 3 ) setTimeout( printStep1, 1000 );
              };
      
          printStep1();
      });
      

      没有 JQuery:

      document.querySelector('input').addEventListener( "click", function () {
      
          var times = 0,
              printStep1 = function(){
                  document.querySelector('div').innerHTML = 'Step 1';
                  setTimeout( printStep2, 1000 );
              },
              printStep2 = function(){
                  document.querySelector('div').innerHTML = 'Step 2';
                  if( times++ < 3 ) setTimeout( printStep1, 1000 );
              };
      
          printStep1();
      });
      

      【讨论】:

        【解决方案5】:

        使用超时连续执行一系列步骤的通用方法:

        var steps = [
            {
                timeout: 2000,
                action: function() { jQuery('div').html('Step 1'); },
            },
            {
                timeout: 1000,
                action: function() { jQuery('div').html('Step 2'); },
            },
            {
                timeout: 1000,
                action: function() { jQuery('div').empty(); },
            }
        ];
        
        function executeSteps(times, index) {
            index = index || 0;
            var task = steps[index];
            if(index > 0 || times > 0) {
                if(index == 0) times--;
                task.action();
                setTimeout(function() { 
                    executeSteps(times, (index + 1) % steps.length);
                }, task.timeout);
            }
        }
        
        jQuery('input').click(function () { 
            executeSteps(3);
        });
        

        JSFiddle

        【讨论】:

          【解决方案6】:

          递归方法答案:

          function recursiveSteps(config) {
              var _step = config.step;
              config.step++;
          
              jQuery('div').html('Step 1');
              setTimeout(function() { 
                  jQuery('div').html('Step 2');
              },2000);
              setTimeout(function() { 
                  jQuery('div').empty();
                  },3000);
              if(config.step < 3) setTimeout(function() {recursiveSteps(config);}, config.step*1000);
              //you could put an "end" step here if needed
          
          }
          
          jQuery('input').click(recursiveSteps);
          

          【讨论】:

          • 其实不行:递归调用应该在最后一个回调函数中。这里被立即调用。
          • 很容易修复,我不确定我到底错过了什么,但如果它应该在调用递归之前超时,这很容易通过添加另一个超时来修复......只是没有在这里使用循环的充分理由,递归更好。
          • 我的意思是函数 recursiveSteps 会立即调用自身,因此它会连续创建 4 个回调,从而产生 OP 试图避免的相同问题
          • 啊,是的,它需要在 recursiveSteps 调用上再次超时
          猜你喜欢
          • 1970-01-01
          • 2016-07-04
          • 1970-01-01
          • 1970-01-01
          • 2021-03-06
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-04-20
          相关资源
          最近更新 更多