简体   繁体   中英

How to add delay between each iteration in Javascript For loop

I'm creating a Simon game for FreeCodeCamp and I'm trying to use a for loop to play an audio sound for each piece of the pattern.

The code I have now plays the audio all at once which won't work for the game. How can I change this code so that it will play each audio sound individually with a .5 second break in between?

Here's the function that plays the audio and adds visual effect

function playAudio() {
    if (colorColor === "red") {
      red.play();
      $('#red').effect("highlight", { color: colorColor }, 500);
    } else if (colorColor === "blue") {
      blue.play();
      $('#blue').effect("highlight", { color: colorColor }, 500);
    } else if (colorColor === "green") {
      green.play();
      $('#green').effect("highlight", { color: colorColor }, 500);
    } else if (colorColor === "yellow") {
      yellow.play();
      $('#yellow').effect("highlight", { color: colorColor }, 500);
    }
}

This is the function where I believe the issue is.

function playPattern() {
    for (var i = 0; i < pattern.length; i++) {
      colorColor = pattern[i];
      setTimeout(playAudio, 500);
    }
  setTimeout(random, 750);
}

And here's the random() function only because it is called within playPattern()

function random() {
    randomColor = colors[Math.floor(Math.random() * colors.length)];
    colorColor = randomColor;
    colorColor = colorColor.slice(1);
    pattern.push(colorColor);

    count++;
    if (count < 10) {
      document.getElementById("count").innerHTML = "0" + count;
    } else {
      document.getElementById("count").innerHTML = count;
    }

    playAudio();

    pickCount = 0;
    userPick = [];
}

Thanks!

Assuming your audios are all the same length:

var audioLength = 3000; // length of audio in ms

for (var i = 0; i < pattern.length; i++) {
  colorColor = pattern[i];
  setTimeout(playAudio, (i * (audioLength + 500));
}

In your code where you are doing:

for (var i = 0; i < pattern.length; i++) {
  colorColor = pattern[i];
  setTimeout(playAudio, 500);
}

What happens is that you basically define to run playAudio in 500ms for all patterns. the for loop is processed immediately and 500ms later all audios are being played together.

What you want is to call the next timeout only when the playAudio function ends.

let i = 0;
function playAudio() {
    i++;
    colorColor = pattern[i];
    if (colorColor === "red") {
      red.play();
      $('#red').effect("highlight", { color: colorColor }, 500);
    } else if (colorColor === "blue") {
      blue.play();
      $('#blue').effect("highlight", { color: colorColor }, 500);
    } else if (colorColor === "green") {
      green.play();
      $('#green').effect("highlight", { color: colorColor }, 500);
    } else if (colorColor === "yellow") {
      yellow.play();
      $('#yellow').effect("highlight", { color: colorColor }, 500);
    }
    if (i < pattern.length) {
        setTimeout(playAudio, 500);
    }
}

function random() {
    randomColor = colors[Math.floor(Math.random() * colors.length)];
    colorColor = randomColor;
    colorColor = colorColor.slice(1);
    pattern.push(colorColor);

    count++;
    if (count < 10) {
      document.getElementById("count").innerHTML = "0" + count;
    } else {
      document.getElementById("count").innerHTML = count;
    }

    playAudio();

    pickCount = 0;
    userPick = [];
}

setTimeout(random, 750);

I based my answer on this answer .

What I did was to preload audio files by creating an object with the titles as keys and with an object value which contains the files' source and if it's loaded already:

const notes = ['do', 're', 'mi', 'fa', 'sol', 'la', 'ti'];
const audioFiles = notes.reduce((obj, title) => {
  obj[title] = {
    src: `notes/${title}.wav`,
    loaded: false,
  };

  return obj;
}, {});

// Preload files.
Object.entries(audioFiles).forEach(([title, details]) => {
  const audio = new Audio();

  audio.addEventListener('canplaythrough', () => {
    details.loaded = true;
  });

  audio.src = details.src;
});

The playPlaylist function takes a playlist (array of titles) and an object containing the audio files:

const playAudio = function playAudio(player, src) {
  player.src = src;
  player.play();
};

const playPlaylist = function playPlaylist(playlist, files) {
  // TODO: Check if files in playlist are already loaded.
  // while (!filesLoaded) { ... }

  const player = new Audio();
  let audioIndex = 0;

  player.addEventListener('ended', () => {
    audioIndex++;

    if (audioIndex >= playlist.length) {
      return;
    }

    setTimeout(() => {
      playAudio(player, files[playlist[audioIndex]].src);
    }, 500);
  });

  playAudio(player, files[playlist[audioIndex]].src);
};

Then just provide the playlist and pass the audioFiles object.

// Play sample playlist.
const playlist = 'do,mi,fa,sol,do,re,la,fa,mi'.split(',');
playPlaylist(playlist, audioFiles);

Just change the files and file paths to test.

You need to consider how much the song lasts, and include that on your timeout, for example if red lasts 1 min, you need to set your next timeout to setTimeout(blue, 60*1000 + 500) , something like this :

function playAudio(color) {
    if (colorColor === "red") {
      red.play();
      $('#red').effect("highlight", { color: colorColor }, 500);
      setTimeout(function () {
         playAudio("blue");
      },60*1000 + 500);
    } else if (colorColor === "blue") {
      blue.play();
      $('#blue').effect("highlight", { color: colorColor }, 500);
      setTimeout(function () {
         playAudio("green");
      },60*1000 + 500);
    } else if (colorColor === "green") {
    //.... ETC

You don't need a for, just some recursion

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM