Как я могу сделать цикл в Javascript, который будет устанавливать таймауты из массива? - PullRequest
0 голосов
/ 12 июня 2018

Фон (Вы можете пропустить это)

Я работаю над веб-приложением, которое анимирует артикуляцию английских фонем во время воспроизведения звука.Он основан на Интерактивном сагиттальном разрезе Даниэля Керри Холла , и можно найти первую попытку здесь .

В следующей версии я хочу, чтобы каждая фонема имелаэто собственные моменты времени анимации, которые определены в массиве, который, в свою очередь, включен в переменную объекта.

Ради простоты этого поста я переместил переменную массива синхронизации из объекта вfunction.

Задача

Я настроил цикл 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 голосов
/ 12 июня 2018

Я бы предложил использовать замыкание функции следующим образом:

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

В этом примере вы оборачиваете фактическую функцию в функцию-обертку, которая захватывает переменную и передает значение.

0 голосов
/ 12 июня 2018

Цикл 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) {

});

Но вы можете использовать Синтаксис функции стрелки вместо определения обратного вызова с помощью function(e,i) { ... } вы определяете его с помощью: (e,i) => { ... }.Это все!Тогда код будет выглядеть так:

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

});

Этот синтаксис является более коротким способом определения вашего обратного вызова.Есть некоторые различия, хотя .

0 голосов
/ 12 июня 2018

Форс завершается до завершения функции 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

...