详解setTimeout、setInterval两大计时器

181 阅读3分钟

众所周知,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'); } 将不再执行。

运行结果

image.png

总结

通过本文,我们学习了两大计时器的概念,并展示了如何使用 setTimeout 实现 setInterval 的功能

理解这篇文章对于面试中常考的定时器问题有很大的帮助,如果觉得有所收获的话不妨留下你的点赞与关注哦。