Запуск различных setTimeouts в последовательности

1

У меня есть множество разных "нет". мин ", и я хотел бы сделать обратный отсчет для них один за другим:

const steps = [{
    label: "Label1",
    time: 4
  },
  {
    label: "Label2",
    time: 2
  },
  {
    label: "Label3",
    time: 1
  }
];

function countdownTimer(time) {

  setTimeout(function () {

    console.log("DONE");

  }, parseInt(time + "000"));
}

function start() {

  steps.forEach(function (step) {
    countdownTimer(step.time);
  });

}

Однако, как и в случае с параметром setTimeout, все они, похоже, работают одновременно с самым коротким временем отображения.

Как заставить setTimeout запускать их последовательно, то есть отображать 4, 2, затем 1?

Это код:

https://codepen.io/aguerrero/pen/yqqMVX

  • 0
    Передайте индекс каждого таймера в countdownTimer(step.time, index) и используйте его как время для setTimeout .
  • 0
    codepen.io/legenjerry/pen/jppBZw?editors=1111
Показать ещё 1 комментарий
Теги:
settimeout
foreach
asynchronous

5 ответов

2

Ваша функция должна ждать, пока таймер обратного отсчета закончится до начала другого, поэтому forEach не будет работать. forEach будет работать, только если вы добавляете время предыдущего объекта к текущему таймеру, что кажется немного слишком большим. Дождитесь окончания каждого таймера, затем запустите следующий. Используйте обратный вызов для этого:

function countdownTimer(obj, callback) {
    setTimeout(function(){ 
        console.log(obj.label + ": DONE");
        callback();                                   // when the current cound down is done call callback to start the next one
    }, parseInt(obj.time + "000"));
}

var start = (function () {                            // wrapping in an IIFE to not pollute the global scope with the index variable. You can get rid of it if you want
    var index = 0;                                    // since we are not using a regular loop nor forEach, we need an index to keep track of the current step
    return function next() {                          // this function is called for each step
        if(index < steps.length) {                    // it check if there still a step in the array steps
            countdownTimer(steps[index], next);       // if so it starts its count down timer, telling it to call next once it finished
            index++;
        }
    };
})();

Пример:

const steps = [ { label: "Label1",  time: 4 }, { label: "Label2", time: 2 }, { label: "Label3", time: 1 } ];

function countdownTimer(obj, callback) {
    setTimeout(function(){ 
        console.log(obj.label + ": DONE");
        callback();
    }, parseInt(obj.time + "000"));
}

var start = (function () {
    var index = 0;
    return function next() {
        if(index < steps.length) {
            countdownTimer(steps[index], next);
            index++;
        }
    };
})();

console.log("Starting...");
start();
  • 1
    Это приятно. Мне это очень нравится.
1

Если вы хотите использовать современный ES6-способ async/wait, вы можете использовать следующий фрагмент

const steps = [
	{
		label: "Label1",
		time: 4
	},
	{
		label: "Label2",
		time: 2
	},
	{
		label: "Label3",
		time: 1
	}
];

const wait = (ms) => {
    return new Promise((resolve) => {
        setTimeout(() => resolve(ms), ms)
    });
};

const start = async () => {
    const startTime = performance.now();

    // use ES6 iterator of the steps array and loop (iterate) over it
    // destructuring is used to obtain index and step
    // for loop is used since .forEach is not awaiting for the callback
    for (const [index, step] of steps.entries()) {
        // await the callback of the wait function
        // and console.log resolved statement
        console.log('${step.label} done in ${await wait(step.time * 1000)} ms');
    }

    console.log('Done in ${performance.now() - startTime}');
};

start();

Я использую цикл for, а не forEach, так как последний не ожидает обратного вызова.

1

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

const steps = [{label: "Label1",time: 4},{label: "Label2",time: 2},{label: "Label3",time: 1}];

function countdownTimer(i) {
  if (i >= steps.length) return 
  setTimeout(function() {
    console.log("DONE WITH", steps[i].label);
    countdownTimer(i + 1)
  }, steps[i].time * 1000);    
}

countdownTimer(0)

Если вам нужно вызвать countdownTimer из цикла, вы можете отслеживать накопленное время и добавлять его к следующему вызову:

const steps = [{label: "Label1",time: 4},{label: "Label2",time: 2},{label: "Label3",time: 1}];

function countdownTimer(time) {
  setTimeout(function() {
    console.log("DONE");
  }, time * 1000);
}

function start() {
  let time = 0                         // keep track of time
  steps.forEach(function(step) {
    countdownTimer(step.time + time);  // add it to call
    time += step.time                  // increase for next call
  }); 
}
start()
0

Мы можем решить это, используя async await

const steps = [{
    label: "Label1",
    time: 4
  },
  {
    label: "Label2",
    time: 2
  },
  {
    label: "Label3",
    time: 1
  }
];

// I added 'label' parameter to make it easier to see it works
function countdownTimer(time, label) {

  // need to make it as promise function for 'await' to work later
  return new Promise((resolve, reject) => {
      setTimeout(function () {

        console.log('${label} DONE');
        resolve(); // resolve the promise

      }, parseInt(time + "000"));
  });
}

// specify async because we use await inside the loop
async function start() {
  for (const step of steps) {
    await countdownTimer(step.time, step.label);
  };
}

start();
0

Это один из способов сделать это

 const output = document.getElementById("result");
 const steps = [
    {
        label: "Label1",
        time: 1
    },
    {
        label: "Label2",
        time: 2
    },
    {
        label: "Label3",
        time: 3
    }
];
const processStep = (step) => {
  return new Promise((resolve, reject) => {
    if (!step) { resolve(false); }
    let timeout = parseInt(step.time + "000");
    setTimeout(() => { 
        console.log("DONE", timeout);
        output.innerHTML = "Step " + step.label + " completed after " + step.time + " seconds";
        resolve(step);
    }, timeout);
  });
};
const processSteps = (steps) => {
  let currentStep = steps.shift();
  if (!currentStep) { return ; }
  console.log("processing step", currentStep);
  processStep(currentStep).then(() => {
    processSteps(steps);
  });
};

processSteps(steps);
 <section id="result"></section>

Ещё вопросы

Сообщество Overcoder
Наверх
Меню