如何在vanilla javascript中一个接一个地运行多个计时器函数

Con*_*nda 1 javascript setinterval

假设我想一个接一个地运行多个计时器函数,即首先一个函数运行 5 分钟,然后在第一个倒计时完成后,另一个计时器开始再运行 2 分钟。

我实现了定时器功能如下

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

Run Code Online (Sandbox Code Playgroud)

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

timer(15);
timer(5);
Run Code Online (Sandbox Code Playgroud)

我得到的输出为

15
5
14
4
13
3
11
1
10
0
9
8
.
.
0
Run Code Online (Sandbox Code Playgroud)

然而我想要的输出是

15
14
.
.
2
1
0
5
4
3
2
1
0
Run Code Online (Sandbox Code Playgroud)

VLA*_*LAZ 5

问题是你的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));
Run Code Online (Sandbox Code Playgroud)

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

timer(5, 
  () => timer(4, 
    () => timer(3, 
      () => timer(2, 
        () => timer(1)
      )
    ) 
  )
);
Run Code Online (Sandbox Code Playgroud)

或者一行(为了好玩):

timer(5, () => timer(4, () => timer(3, () => timer(2, () => timer(1)))));
Run Code Online (Sandbox Code Playgroud)

承诺

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

timer(5, 
  () => timer(4, 
    () => timer(3, 
      () => timer(2, 
        () => timer(1)
      )
    ) 
  )
);
Run Code Online (Sandbox Code Playgroud)

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

timer(5)
  .then(() => timer(4))
  .then(() => timer(3))
  .then(() => timer(2))
  .then(() => timer(1));
Run Code Online (Sandbox Code Playgroud)

不再筑巢。

异步/等待

这其实又是Promise。不过是乔装打扮而已。如果一个函数返回一个 Promise,你可以await等待 Promise 被解析,然后执行下一行代码。但是,您只能在函数await内部使用async,因为await实际上会将您的代码转换为幕后的 Promise。从功能上来说,没有什么区别,但您可以以不同的方式构建代码:

timer(5, () => timer(4, () => timer(3, () => timer(2, () => timer(1)))));
Run Code Online (Sandbox Code Playgroud)