如何在Javascript中创建一个for循环来设置数组的超时?

时间:2018-06-12 10:19:30

标签: javascript arrays for-loop settimeout

背景(您可能想跳过此内容)

我正在制作一个网络应用程序,可以在播放声音的同时激发英语音素的清晰度。它基于Interactive Sagittal Section by Daniel Currie Hall,可以找到第一次尝试here

对于下一个版本,我希望每个音素都拥有自己的动画时序,这些时序在数组中定义,而数组又包含在对象变量中。

为了简化本文,我将时序数组变量从对象移动到函数中。

问题

我设置了一个for循环,我认为它会引用索引i和数组t来设置每个setTimeout的毫秒数。

function animateSam() {

  var t = [0, 1000, 2000, 3000, 4000];
  var key = "key_0";

  for (var i = 0; i < t.length; i++) {
    setTimeout(function() {
      console.log(i);
      key = "key_" + i.toString();
      console.log(key);

      //do stuff here

    }, t[i]);
  }
}

animateSam()

然而,似乎毫秒是由函数到达堆栈顶部时发生的任何事情设置的。

问题:有没有可靠的方法来设置数组的毫秒数?

3 个答案:

答案 0 :(得分:3)

for在setTimeout函数完成之前结束,所以你必须在闭包内设置超时:

function animateSam(phoneme) {

  var t = [0,1000,2000,3000,4000];

  for (var i = 0; i < t.length; i++) {
    (function(index) {
        setTimeout(function() {
            alert (index);
            key = "key_" + index.toString();
            alert (key);

            //do stuff here

        }, t[index]);
    })(i);
  }
}

在这里您可以解释为什么会发生这种情况: https://hackernoon.com/how-to-use-javascript-closures-with-confidence-85cd1f841a6b

答案 1 :(得分:3)

for循环将在第一个setTimeout被触发之前循环所有元素,因为它具有异步性质。当您的循环运行时,i将等于5。因此,您可以获得相同的输出五次。

您可以使用Array类中的方法,例如.forEach

这确保了函数被包含在内。

[0, 1000, 2000, 3000, 4000].forEach((t, i) => {
  setTimeout(function() {

    console.log(i);
    console.log(`key_${i}`);

    //do stuff here

  }, t)
});

旁注:我会建议你在工作/调试时使用alert,因为它真的很让人困惑和讨厌。最好是使用简单的console.log

关于代码的更多说明:

.forEach接收要在每个元素上运行的回调函数作为主要参数。这个回调本身可以带两个参数(在我们之前的代码t中是当前元素的值,i是当前元素在数组中的索引):

Array.forEach(function(value, index) {

});

但您可以使用arrow function syntax,而不是使用function(e,i) { ... }定义回调,而不是使用(e,i) => { ... }定义回调。就这样!然后代码看起来像:

Array.forEach((value,index) => {

});

此语法是定义回调的较短方法。有some differences though

答案 2 :(得分:1)

我建议使用函数闭包,如下所示:

function animateSam(phoneme) {

  var t = [0,1000,2000,3000,4000];

  var handleAnimation = function (idx) {
    return function() {
      alert(idx);
      key = "key_" + idx.toString();
      alert(key);
      //do stuff here
    };
  }
  for (var i = 0; i < t.length; i++) {
    setTimeout(handleAnimation(i), t[i]);
  }
}

在本例中,您将实际函数包装在一个包装函数中,该函数捕获变量并传递值。