我有一系列不同的“否”。分钟”,我想一个接一个地倒计时:
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?
这是codepen:
答案 0 :(得分: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)
您可以让计时器在完成后调用该函数,并为下一个索引传递参数:
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()
答案 2 :(得分:1)
如果您想使用现代的ES6异步/等待方式,可以使用以下代码段
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,因为最后一个没有等待回调。
答案 3 :(得分: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>
答案 4 :(得分: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();