如何 运行 多个定时器功能,一个接一个地在香草 javascript

How to run multiple timer functions, one after the other in vanilla javascript

假设我想要 运行 多个计时器功能,一个接一个,即首先一个功能 运行s 持续 5 分钟,然后在第一个倒计时完成后,另一个计时器开始 运行 再等 2 分钟。

我实现了如下定时器功能

function timer(count) {
    console.log(count)
    let counter = setInterval(() => {
        count = count - 1;
        if (count < 0) {
            clearInterval(counter);
            return;
        }
        console.log(count)
    }, 1000);
}

然后当我用不同的参数调用这个函数两次时

timer(15);
timer(5);

我得到的输出是

15
5
14
4
13
3
11
1
10
0
9
8
.
.
0

但是我想要的输出是

15
14
.
.
2
1
0
5
4
3
2
1
0

您必须等待第一个调用结束才能进行第二个调用。一个简单的方法是用 Promise 包装 setInterval 调用,并在计数器达到 0 时调用 resolve

使用 Promise(ES6 但在大多数浏览器上运行,IE 需要 Polyfill)

timer(15).then(timer.bind(0, 5));

function timer(count) {
  console.log(count);
  return new Promise(function(resolve) {
    var intervalID = setInterval(function() {
      count = count - 1;
      if (count < 0) {
        clearInterval(intervalID);
        resolve();
        return;
      }
      console.log(count);
    }, 1000);
  });
}

使用 asyncawait (ES7)

(async function() {
  await timer(15);
  await timer(5);
})();

async function timer(count) {
  console.log(count);
  return new Promise(resolve => {
    let intervalID = setInterval(() => {
      count = count - 1;
      if (count < 0) return resolve(), clearInterval(intervalID);
      console.log(count);
    }, 1000);
  });
}

在现代 javascript 中处理异步内容的最佳方式是 promises 和 async/await。但是因为wait没有默认的方式我们需要自己写。这很简单:

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

现在我们可以用它来写你的 timerasync/await 并使它 return 成为 Promise:

async function timer(count) {
    for(let i = count; i > 0; i--) {
        await wait(1000);
        console.log(i);
    }
}

而且在异步函数中多次使用 timer 并不容易:

await timer(15);
await timer(5);

所以完整的代码是:

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

async function timer(count) {
    for(let i = count; i > 0; i--) {
        await wait(1000);
        console.log(i);
    }
}

async function multipleTimers() {
    await timer(15);
    await timer(5);
}

multipleTimers();

您似乎希望第二个 timer 函数仅在第一个函数的所有间隔都完成时才执行。这绝对不是你实施的。实际上,第一个函数被调用,它设置了时间间隔,然后结束。主代码不会被间隔的设置打断,所以它会继续执行,调用第二个timer函数。

根据您想要的输出,您肯定需要一个异步函数,以了解第一个间隔何时执行完毕。以下是您可以尝试的方法:

function timer(count) {
   return new Promise(resolve => {
       console.log(count)
        let counter = setInterval(() => {
            count = count - 1;
            if (count < 0) {
                clearInterval(counter);
                resolve();
                return;
            }
            console.log(count)
        }, 1000);
    }
}

timer(15).then(() => {
    timer(5);
});

如果你已经在一个异步函数中,最后一位也可以写成:

await timer(15);
timer(5);

你应该阅读一下 async functions。真的很值得。

问题是您的 timer 函数会立即启动计时器。定时器是异步的,所以当你调用它两次时,你只是立即启动两个定时器并且它们 运行 并行。

如果你想要在完成之后发生某事,那么你必须明确说明。您有两个选择:

回调

这是处理异步代码的稍微“老”的风格。你调用一个稍后会做某事的函数,然后给它一个函数作为完成后要做什么的参数:

function timer(count, callback = () => {}) { //<-- take callback
 //if callback is not supplied, it's going to be an empty function
       
    console.log(count)
    let counter = setInterval(() => {
        count = count - 1;
        if (count < 0) {
            clearInterval(counter);
            callback(); //<-- run callback after this timer is finished
            return;
        }
        console.log(count)
    }, 1000);
}

//run a timer for 15 then a timer for 5
timer(15, () => timer(5));

有效 但过度使用回调会导致所谓的 callback hell。这里的例子也很容易出现这种情况,例如,如果你想 运行 一个计时器 5,然后是 4,然后是 3,然后是 2,然后是 1,你最终会得到这个:

timer(5, 
  () => timer(4, 
    () => timer(3, 
      () => timer(2, 
        () => timer(1)
      )
    ) 
  )
);

或一行(为了好玩):

timer(5, () => timer(4, () => timer(3, () => timer(2, () => timer(1)))));

承诺

Promises 是一种处理异步操作的新方法,有助于保持代码简洁。

function timer(count) {
  console.log(count)

  return new Promise(resolve => { //return a Promise
    let counter = setInterval(() => {
      count = count - 1;
      if (count < 0) {
        clearInterval(counter);
        resolve(); //it is resolved when the count finishes
        return;
      }
      console.log(count)
    }, 1000);
  });
}

//run a timer for 15 then a timer for 5
timer(15)
  .then(() => timer(5));

Promises 提供帮助的一件事是回调地狱,现在如果你想 运行 定时器 5,然后是 4,然后是 3,然后是 2,然后是 1,你会得到很多更合理的代码:

timer(5)
  .then(() => timer(4))
  .then(() => timer(3))
  .then(() => timer(2))
  .then(() => timer(1));

不再嵌套。

async/await

这其实又是Promises。而是带着伪装。如果一个函数 returns 一个 Promise,你可以 await 它等到 Promise 被解决,然后执行下一行代码。您只能在 async 函数中使用 await,但是因为 await 实际上会在幕后将您的代码转换为 Promise。在功能上,差别不大,但您可以以不同的方式构建代码:

function timer(count) {
  console.log(count)

  return new Promise(resolve => { //return a Promise
    let counter = setInterval(() => {
      count = count - 1;
      if (count < 0) {
        clearInterval(counter);
        resolve(); //it is resolved when the count finishes
        return;
      }
      console.log(count)
    }, 1000);
  });
}

//run a timer for 15 then a timer for 5
async function main() { //you can only use `await` in async funcions 
  await timer(15);
  await timer(5);
}
/* the above will actually be transformed behind the scenes into:
timer(15)
  .then(() => timer(5));
*/

main();