У меня есть множество разных "нет". мин ", и я хотел бы сделать обратный отсчет для них один за другим:
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?
Это код:
Ваша функция должна ждать, пока таймер обратного отсчета закончится до начала другого, поэтому 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();
Если вы хотите использовать современный 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, так как последний не ожидает обратного вызова.
Вы можете просто вызвать таймер функции, когда она будет выполнена, и передать параметр для следующего индекса:
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()
Мы можем решить это, используя 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();
Это один из способов сделать это
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>
countdownTimer(step.time, index)
и используйте его как время дляsetTimeout
.