2

I have a array of different objects, each object contain a time property of different values.

I would like to loop for this array, inside have a setTimeout function, the time would be the time property of each object.

Therefore, the result I want is

  1. after 10s, print the name of first object
  2. after 20s, print the name of 2nd obj.
  3. ...
  4. ...
  5. after 5s, print the name of last obj.

However, the below code will execute in an accumulation of total 20s, which is print obj 3 & 5 when time = 5s, after 5s, print obj 1, after 10s, print obj 2 & 4.

const data = [
{name: "Warm up", timeFormat: "00:10", time: 10},
{name: "High interval", timeFormat: "00:20", time: 20},
{name: "Low Interval", timeFormat: "00:05", time: 5},
{name: "High interval", timeFormat: "00:20", time: 20},
{name: "Low Interval", timeFormat: "00:05", time: 5},
]

function renderTimer(data) {
  for (let i = 0; i < data.length; i++) {
    const eachName = data[i].name;
    const eachTime = data[i].time;

    setTimeout(() => {
      console.log(eachName);
    }, eachTime * 1000);
  }
}

renderTimer(data);

What is the problem of my code? OR any other ways to achieve the result I want?

Thanks so much!

2
  • just to be sure, you want the timeout of the object to start after the previous object's timeout ended ? Commented Aug 10, 2020 at 13:59
  • yes, after time in obj 1 finish, execute obj 2 basing on its time Commented Aug 10, 2020 at 14:06

4 Answers 4

3

What happens, is that the program races through the for loop, and sets up the Timeouts almost immediatley relative to t=0s. If you want to use setTimeout(), you have to cumulate the timing yourself:

const data = [
{name: "Warm up", timeFormat: "00:10", time: 10},
{name: "High interval", timeFormat: "00:20", time: 20},
{name: "Low Interval", timeFormat: "00:05", time: 5},
{name: "High interval", timeFormat: "00:20", time: 20},
{name: "Low Interval", timeFormat: "00:05", time: 5},
]

function renderTimer(data) {
  var timing = 0;
  for (let i = 0; i < data.length; i++) {
    const eachName = data[i].name;
    timing += data[i].time;

    setTimeout(() => {
      console.log(eachName);
    }, timing * 1000);
  }
}

renderTimer(data);

Sign up to request clarification or add additional context in comments.

1 Comment

Only answer with an explanation.
2

you can do it using async/await and Promises

it works using the fact that await new Promise(...) stop the current function until the Promise is fulfilled

const data = [
  {name: "Warm up", timeFormat: "00:10", time: 10},
  {name: "High interval", timeFormat: "00:20", time: 20},
  {name: "Low Interval", timeFormat: "00:05", time: 5},
  {name: "High interval", timeFormat: "00:20", time: 20},
  {name: "Low Interval", timeFormat: "00:05", time: 5},
]

async function renderTimer(data) {
  for (let i = 0; i < data.length; i++) {
    const eachName = data[i].name;
    const eachTime = data[i].time;

    await new Promise(res => {
      setTimeout(() => {
        console.log(eachName);
        // resolve the promise once the log is done
        res()
      }, eachTime * 100)}
    )
    // can't arrive here until the setTimeout is finalized
  }
}

renderTimer(data);

Comments

2

You could also do it in a recursive function like so:

const data = [
{name: "Warm up", timeFormat: "00:10", time: 10},
{name: "High interval", timeFormat: "00:20", time: 20},
{name: "Low Interval", timeFormat: "00:05", time: 5},
{name: "High interval", timeFormat: "00:20", time: 20},
{name: "Low Interval", timeFormat: "00:05", time: 5},
]

const renderTimer = (data, i = 0) => {
  setTimeout(() => {
    console.log(data[i].name);
    
    // Call for next data
    if(i < data.length - 1)
      renderTimer(data, i + 1);
  }, data[i].time * 1000);
}

renderTimer(data);

1 Comment

you could set a default value of 0 to your i
1

You can do it like this :

const data = [
{name: "Warm up", timeFormat: "00:10", time: 10},
{name: "High interval", timeFormat: "00:20", time: 20},
{name: "Low Interval", timeFormat: "00:05", time: 5},
{name: "High interval", timeFormat: "00:20", time: 20},
{name: "Low Interval", timeFormat: "00:05", time: 5},
]

var time = 0;

function renderTimer(data) {
  for (let i = 0; i < data.length; i++) {
    const eachName = data[i].name;
    time += data[i].time;
    const eachTime = time * 1000;

    setTimeout(() => {
      console.log(eachName);
    }, eachTime);
  }
}

renderTimer(data);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.