setTimeout and setInterval in async JavaScript

非同步 JavaScript 时间操纵之术: setTimeout 与 setInterval

学会非同步操纵时间

在介绍到同步与非同步代码时,时常会以 setTimeout 以及 setInterval 来模拟程序被非同步的执行,本文将会详细讲解其背后原理与实际操作。

setTimeout

如果想要在一段时间后执行一段代码会使用 setTimeout🔗 ,它是存在于 DOM API🔗 中的方法,可达成:「在 x 毫秒后,执行 y 函数」的功能。

setTimeout(回呼函式, 等待毫秒数);
// 写法一:使用具名函数撰写
function callback() {
// ...
}
setTimeout(callback, 1000);
// 写法二:使用匿名函数撰写
setTimeout(() => {
// ...
}, 1000);

拿一些更为实际的案例来展示:小明得知晚餐煮好了,但他要打电脑,所以和家人说再一分钟后就离开电脑去吃晚餐。

// Input 输入
// 告知家人再一分钟
console.log('知道了,再等一分钟就过去');
// 倒数一分钟后小明去吃晚餐
setTimeout(() => {
console.log('小明去吃晚餐了');
}, 60 * 1000);
// Output 输出
'知道了,再等一分钟就过去'(经过一分钟后);
('小明去吃晚餐了');

setInterval

setTimeout 只会执行一次,而 setInterval 则是在输入的间隔时间内不断重复执行输入的函数,它们有着一模一样的语法:

setInterval(回呼函式, 等待毫秒数);
// Input 输入
let timePassed = 0
setInterval(() => {
timePassed += 1
console.log(timePassed)
}, 1000)
// Output 输出
1
2
3
...

取消倒数

要清除时间倒数,可以使用 clearInterval(timerId)clearTimeout(timerId)。每当使用以上方法时,就会获得一笔「标识符 ID」,放入 clearTimeoutclearInterval 函数中即可终止整个计划调用。

const timerId = setTimeout(...);
clearTimeout(timerId);
const timerId = setInterval(...);
clearInterval(timerId);

背后理论

一切都是如此的自然,但仔细想想似乎有些不合理的事情?

浏览器中 JavaScript 是以单一执行线程来同步执行程序的,白话来说就是一次只能执行一件事,那这样要如何才能做到同时间「等待」又「执行后续程序」呢?

实际上,JavaScript 确实有执行 setTimeout ,只是将「等待」这件事交给 Web API 处理,详细可以在文章底部有我写的另一篇文章:从动图轻松入门非同步 JavaScript 可参考。

零延迟 Zero Delay

当把 setTimeout 的延迟设置为 0 秒时,并不意味着回调函数中的内容会立即执行,实际上要考虑到 Call Stack 中还有没有事要完成,该参数仅代表「请求执行环境处理所需的最少等待时间,而非一个保证时间。」

所以进行精确时间的等待最好不要使用 setTimeout

// Input 输入
setTimeout(() => {
console.log('bar');
}, 0);
console.log('foo');
// Output 输出
('foo');
('bar');

参考资料