【问题标题】:How can I make a for loop in Javascript that will set timeouts from an array?如何在 Javascript 中创建一个从数组设置超时的 for 循环?
【发布时间】:2023-04-07 20:49:01
【问题描述】:

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

我正在开发一个网络应用程序,它可以在播放声音的同时为英语音素的发音设置动画。它基于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()

但是,似乎毫秒是由函数到达堆栈顶部时碰巧设置的。

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

【问题讨论】:

  • 使用let 代替var,或者更好的是,使用数组方法代替for 循环。
  • 您是否尝试将i 移动到for loop body 内的另一个变量中,或者更像是将毫秒数复制到另一个变量中?喜欢var x = t[i]
  • 除了let而不是var,为什么不用i * 1000代替数组的方式呢?硬编码什么是简单的数学似乎违反直觉..
  • @ivan, baao:这只是一个例子——音素没有间隔一秒的时间,他真正的用例不是那个数组。
  • @Dominic Tobias 我分配了“var key;”在 for 循环之外,但忘记将其复制到我的简化示例中。感谢您的提醒,我已经修改了问题。

标签: javascript arrays for-loop settimeout


【解决方案1】:

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

【讨论】:

  • 顺便说一句,这是一道经典的求职面试题
  • 谢谢@A。 Llorente - 特别是对于解释的链接。即使表面上解决了问题,我只是讨厌不理解事物:-)
【解决方案2】:

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) =&gt; { ... } 定义它。就这样!然后代码将如下所示:

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

});

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

【讨论】:

  • 谢谢@Ivan 您的代码实际上更简单,但我不太了解语法(它与我见过的其他 forEach 示例不同),所以我选择了 A. Llorente 的答案更容易应用于我的实际代码。
  • 您对 console.log 的看法是正确的 - 警报让我感到困惑!
  • @Thailandian,没问题。我进行了编辑以解释语法。我希望它能帮助你更好地理解
  • 谢谢@Ivan。我已经完成了对代码的修改,但很高兴它可以作为其他人在这里偶然发现的参考。
【解决方案3】:

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

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]);
  }
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-09-11
    • 2021-09-11
    • 1970-01-01
    • 2019-08-28
    • 2012-09-21
    • 1970-01-01
    相关资源
    最近更新 更多