【问题标题】:setTimeout and array eachsetTimeout 和数组每个
【发布时间】:2013-06-19 05:40:10
【问题描述】:

我对使用 setTimeout 和 each 迭代器感到困惑。如何重写以下内容,以便控制台在延迟 5 秒后输出每个名称?目前,下面的代码会在 5 秒后一次打印所有名称。我想:

1) 等待 5 秒,然后打印 kevin
2) 等待 5 秒,然后打印 mike
3) 等待 5 秒,然后打印 sally

var ary = ['kevin', 'mike', 'sally'];

_(ary).each(function(person){

  setTimeout(function(){
    console.log(person);
  }, 5000);    

});

【问题讨论】:

    标签: javascript underscore.js settimeout each


    【解决方案1】:

    您有三个基本选项:

    1. For 循环 + setTimeout
      ...立即初始化每个人,但根据索引位置错开开始时间,这样他们就不会同时进行。
    2. set<i>Timeout</i> + 条件递归
      ...每隔 n 秒检查一次 - 我会告诉你是否需要再做一次
    3. set<i>Interval</i> + 条件clearInterval
      ... 每隔 n 秒继续运行 - 直到我告诉你停止

    以下是每一个都用一个工作示例来充实:

    1。循环 + setTimeout

    对此有几点说明。这有点像开始接力赛,并提前向每个跑步者发出指令,让他们在 5:00 和 5:02 和 5:04 开始,不管他们身后的人是很久以前完成还是没有完成。还没到。

    此外,您不能使用常规的for i=0 loop,因为 for 运算符没有定义新的函数范围。因此,在每个 for 循环中设置的对象值将适用于迭代。到调用 setTimeout 时,它只会使用最近的值。所以我们需要一个闭包来存储每个循环中的值。我用过Array.prototype.forEach(),但如果你想在 jQuery 或 Underscore 中使用 forEach 实现,那也可以。

    function ArrayPlusDelay(array, delegate, delay) {
     
      // initialize all calls right away
      array.forEach(function (el, i) {
        setTimeout(function() {
            // each loop, call passed in function
            delegate( array[i]);
    
          // stagger the timeout for each loop by the index
          }, i * delay);
      })
     
    }
    
    // call like this
    ArrayPlusDelay(['a','b','c'], function(obj) {console.log(obj)},1000)

    2。 setTimeout + 条件递归

    对于最后两个选项,我们正在创建自己的循环,因此我们必须自己跟踪索引,初始化为零并自始至终递增。

    对于这个,我们将 a) 调用 setTimeout,它将运行一次,b) 在索引位置评估数组,c) 检查数组中是否有更多元素,如果有,从 (一)。

       
    function ArrayPlusDelay(array, delegate, delay) {
      var i = 0
      
      function loop() {
      	  // each loop, call passed in function
          delegate(array[i]);
          
          // increment, and if we're still here, call again
          if (i++ < array.length - 1)
              setTimeout(loop, delay); //recursive
      }
    
      // seed first call
      setTimeout(loop, delay);
    }
    
    // call like this
    ArrayPlusDelay(['d','e','f'], function(obj) {console.log(obj)},1000)

    3。 setInterval + 有条件的clearInterval

    注意:函数setInterval 将永远运行一次。它最初设置时的返回值将提供对间隔的引用,因此它通常与函数 clearInterval 结合使用以选择性地关闭它

    function ArrayPlusDelay(array, delegate, delay) {
      var i = 0
      
       // seed first call and store interval (to clear later)
      var interval = setInterval(function() {
        	// each loop, call passed in function
          delegate(array[i]);
          
            // increment, and if we're past array, clear interval
          if (i++ >= array.length - 1)
              clearInterval(interval);
      }, delay)
      
    }
    
    ArrayPlusDelay(['x','y','z'], function(obj) {console.log(obj)},1000)

    3* 秘密第四选项(最佳选项)

    选项 1 和 2 是有风险的,因为一旦您启动了那辆火车,就无法在路上取消它(除非关闭浏览器)。如果您的委托中有一个大数组或繁重的负载,如果您需要它可能会很好地提供一些资源。通过保存来自setInterval 的引用,我们将可以持续访问迭代函数。我们只需要在调用我们的数组加延迟函数时返回上面的区间对象并保存即可。

    function ArrayPlusDelay(array, delegate, delay) {
      var i = 0
      
       // seed first call and store interval (to clear later)
      var interval = setInterval(function() {
        	// each loop, call passed in function
          delegate(array[i]);
          
            // increment, and if we're past array, clear interval
          if (i++ >= array.length - 1)
              clearInterval(interval);
      }, delay)
      
      return interval
    }
    
    var inter = ArrayPlusDelay(['x','y','z'], function(obj) {console.log(obj)},1000)

    如果我们以后想清除它,只需将其扔到控制台中即可:

    clearInterval(inter);
    

    All 3 Demos in jsFiddle

    类似的堆栈溢出问题:

    【讨论】:

    • #1 效果很好。非常感谢您抽出宝贵时间对每个选项进行充分评论。
    • 惊人的努力,您为我们快速理解该主题所做的努力。
    【解决方案2】:

    您可以创建一个名为 offset 的变量,让计时器为数组中的每个人多等待 5 秒,如下所示:

    var ary = ['kevin', 'mike', 'sally'];
    
    var offset = 0;
    _(ary).each(function(person){
    
      setTimeout(function(){
        console.log(person);
      }, 5000 + offset);    
     offset += 5000;
    });
    

    【讨论】:

    • 您能描述一下这是如何工作的吗?我猜这是因为每个迭代器触发了 3 个“事件”并且每个事件的超时时间分别为 5、10 和 15 秒?我的代码触发了 3 个“事件”,每个事件仅持续 5 秒?
    • 没错。 each 运行了 3 次,每次offset 变大 5000,这样连续每个人的超时时间都晚了 5 秒。
    【解决方案3】:

    你可以的

    var ary = ['kevin', 'mike', 'sally'];
    
    _(ary).each(function(person, index){
    
      setTimeout(function(){
        console.log(person);
      }, index * 5000);    
    });
    

    在不增加 timeout 值的情况下,您将使用完全相同的值初始化所有 setTimeouts(这就是您看到所见的原因)。

    【讨论】:

      【解决方案4】:

      each 通常更适合立即发生的事情。

      相反,如果您不介意更改数组,则可以将其用作队列:

      var ary = ['kevin', 'mike', 'sally'];
      
      setTimeout(function loop() {
          console.log(ary.shift());
      
          if (ary.length)
              setTimeout(loop, 5000);
      }, 5000);
      

      它会在未来 5 秒后继续调用 loop,直到队列中没有任何东西。

      【讨论】:

      • 这不是有点混淆了吗?一个函数 insite 一个函数调用,其名称只能在范围内访问?
      【解决方案5】:

      array.forEach 接受一个回调函数,这意味着它已经创建了一个新的函数范围,并且由于 setTimeout 的性质是非阻塞的,它会立即返回,您只需要在每次迭代时增加回调函数执行的延迟:

      var ary = ['kevin', 'mike', 'sally'];
      
      ary.forEach(function(person, index){
          setTimeout(function(){
              console.log(person);
          }, 5000 * (index + 1));   
      })
      

      如果你想用 for 循环达到同样的效果,你可以使用 IIFE 或 let 关键字

      IIFE 示例:

      var ary = ['kevin', 'mike', 'sally'];
      
      for(var i = 1; i <= ary.length; i++){
          (function(i){
              setTimeout(function(){
                  console.log(ary[i - 1]);
              }, 5000 * i); 
          })(i)
      }
      

      let 关键字示例:[ECMAScript 6]

      var ary = ['kevin', 'mike', 'sally'];
      
      for(let i = 1; i <= ary.length; i++){
          setTimeout(function(){
              console.log(ary[i - 1]);
            }, 5000 * i); 
      }
      

      【讨论】:

        【解决方案6】:

        最简单的方法:

        const ary = ['kevin', 'mike', 'sally'];
        
        ary.forEach((item, index) => {
            setTimeout(() => {
                console.log(item)
            }, index * 5000)
        })
        

        “kevin”的索引为 1,所以会在 5 秒后调用(1 * 5000 ms)

        “mike”的索引为 2,因此会在 10 秒(2 * 5000 毫秒)后调用

        "sally" 的索引为 3,因此它将在 15 秒(3 * 5000 毫秒)后调用

        【讨论】:

          【解决方案7】:

          您可以将setInterval() 与简单的加一计数器一起使用。

          var ary = ['kevin', 'mike', 'sally'];
          
          var i=0;
          setInterval(function(){
              console.log(ary[i]);
              i++;
          }, 5000);
          

          但请注意,这将在 i 大于 2 后开始抛出错误。您需要在那里进行某种验证并确保清除间隔。

          【讨论】:

          • 但您可能希望在数组耗尽时将setInterval 回调到clearInterval
          猜你喜欢
          • 1970-01-01
          • 2013-03-12
          • 1970-01-01
          • 2014-02-01
          • 2017-12-19
          • 2019-02-01
          • 1970-01-01
          • 2014-08-15
          • 1970-01-01
          相关资源
          最近更新 更多