众所周知,JavaScript 是一种单线程的语言,这意味着它一次只能执行一个任务。所有的任务都在一个主线程上按顺序执行。这种特性在处理异步操作时尤为重要,因为异步操作不会阻塞主线程,而是将任务放入事件循环(Event Loop)中等待执行。本文将深入探讨 JavaScript 中的定时器 setTimeout
和 setInterval
,并展示如何使用 setTimeout
实现 setInterval
的功能。
引入
光看文字不好理解,下面我们来看一段代码
setTimeout(function(){
console.log(1);
},0);
console.log(2);
console.log(3);
猜猜这段代码的运行结果是什么呢?会是1,2,3吗?
但实际运行结果却是2、1、3,这是为什么呢? 这就与setTimeout()的规则有关了
setTimeout
setTimeout
是一个异步函数,它会在指定的时间后将回调函数放入事件循环中。这意味着在 setTimeout
被调用后,同步代码会先执行,然后在主线程空闲时,事件循环会处理 setTimeout
的回调函数。
setTimeout(() => {
console.log('This will be executed after 1000ms');
}, 1000);
一定会在指定时间后执行吗?
setTimeout
并不保证一定会在指定时间后立即执行回调函数。这是因为 JavaScript 是单线程的,如果主线程上有其他任务在执行,回调函数会在主线程空闲后才执行。- 因此,
setTimeout
的实际执行时间可能会比指定的时间稍晚。
返回值和清除定时器
setTimeout
返回一个定时器 ID,可以使用clearTimeout
来清除这个定时器,防止回调函数被执行。
const timerId = setTimeout(() => {
console.log('This will not be executed');
}, 1000);
clearTimeout(timerId);
setInterval
setInterval
是一个用于重复执行回调函数的定时器。它会在指定的时间间隔后重复执行回调函数。
setInterval(() => {
console.log('This will be executed every 1000ms');
}, 1000);
如何使用 setTimeout
实现 setInterval
?
我们可以使用 setTimeout
通过递归调用来模拟 setInterval
的行为。
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>手写定时器</title>
</head>
<body>
<script>
function customSetInterval(fn,time){
let intervalId = null;
function loop(){
intervalId=setTimeout(()=>{
fn();
loop();
},time)
}
loop();
return () =>{
clearTimeout(intervalId)
}
}
const interval = customSetInterval(function(){
console.log('hello world')
},1000)
setTimeout(()=>{
interval()
},5000)
</script>
</body>
</html>
分析
HTML 结构
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>手写定时器</title>
</head>
<body>
<script>
// JavaScript 代码将放在这里
</script>
</body>
</html>
JavaScript 代码解释
1. customSetInterval
函数
function customSetInterval(fn, time) {
let intervalId = null;
function loop() {
intervalId = setTimeout(() => {
fn();
loop();
}, time);
}
loop();
return () => {
clearTimeout(intervalId);
}
}
- 参数:
fn
: 要重复执行的回调函数。time
: 每次执行回调函数的时间间隔(以毫秒为单位)。
- 内部变量:
intervalId
: 用于存储setTimeout
返回的定时器 ID。loop
函数:- 这是一个递归函数,它会调用
setTimeout
,并在setTimeout
的回调函数中再次调用自身。 setTimeout
的回调函数会在time
毫秒后执行,执行fn()
后再次调用loop()
,从而实现每隔time
毫秒执行一次fn()
。
- 这是一个递归函数,它会调用
- 返回值:
- 返回一个函数,该函数可以用来清除定时器,停止递归调用。
clearTimeout(intervalId)
用于清除setTimeout
设置的定时器。
2. 使用 customSetInterval
const interval = customSetInterval(function() {
console.log('hello world');
}, 1000);
- 这里调用
customSetInterval
函数,传入一个回调函数和时间间隔1000
毫秒(即 1 秒)。 - 回调函数
function() { console.log('hello world'); }
会在每隔 1 秒执行一次。 customSetInterval
返回一个函数,这个函数存储在interval
变量中,用于清除定时器。
3. 停止定时器
setTimeout(() => {
interval();
}, 5000);
- 这里使用
setTimeout
设置一个定时器,5 秒后执行回调函数。 - 回调函数中调用
interval()
,这个函数会清除之前设置的定时器,停止递归调用。 - 因此,5 秒后,
customSetInterval
设置的定时器会被清除,回调函数function() { console.log('hello world'); }
将不再执行。
运行结果
总结
通过本文,我们学习了两大计时器的概念,并展示了如何使用 setTimeout
实现 setInterval
的功能
理解这篇文章对于面试中常考的定时器问题有很大的帮助,如果觉得有所收获的话不妨留下你的点赞与关注哦。