【问题标题】:Running different setTimeouts in a sequence按顺序运行不同的 setTimeouts
【发布时间】:2018-08-05 15:28:08
【问题描述】:

我有一系列不同的“不”。分钟',我想为他们一个接一个倒计时:

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

【问题讨论】:

标签: javascript asynchronous foreach settimeout


【解决方案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();

【讨论】:

  • 这很好。我非常喜欢它。
【解决方案2】:

您可以让计时器在完成后调用该函数并为下一个索引传递一个参数:

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()

【讨论】:

    【解决方案3】:

    如果你想使用现代 ES6 的 async/await 方式,你可以使用下面的 sn-p

    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,因为最后一个循环没有等待回调。

    【讨论】:

      【解决方案4】:

      这是一种方法

       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);
       &lt;section id="result"&gt;&lt;/section&gt;

      【讨论】:

        【解决方案5】:

        我们可以使用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();

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2019-09-14
          • 1970-01-01
          • 2013-08-13
          • 2012-06-13
          • 2017-06-12
          • 2011-05-27
          相关资源
          最近更新 更多