js中的两种定时器区别是什么以及怎么清除定时器详解

 更新时间:2026年01月12日 10:03:21   作者:董世昌41  
在做项目中难免会碰到需要实时刷新,动画依次出现等等需求,这时候就需要定时器登上我们的代码舞台了,这篇文章主要介绍了js中两种定时器区别是什么以及怎么清除定时器的相关资料,需要的朋友可以参考下

一、开篇:定时器 ——JavaScript 实现异步延迟的核心工具

在 JavaScript 中,作为一门单线程语言,实现异步操作和延迟执行逻辑的核心工具之一就是定时器(Timer)。无论是实现页面倒计时、轮播图自动切换、接口定时轮询,还是避免同步代码阻塞页面渲染,都离不开定时器的加持。

JavaScript 提供了两种核心定时器方法:setTimeout() 和 setInterval(),很多初学者在使用时容易混淆二者的执行逻辑,出现 “定时器无法停止”“执行时机不符合预期” 等问题 —— 比如用setInterval()实现倒计时出现时间跳变、清除定时器后仍有残留执行逻辑。

本文将从两种定时器的核心定义、执行机制、核心区别,到定时器的清除方法、实战避坑指南,结合完整示例深度拆解,帮你彻底掌握 JS 定时器的使用精髓,写出健壮、可控的异步延迟代码。

二、第一部分:认识 JS 的两种核心定时器

1. 一次性定时器:setTimeout ()

(1)核心定义

setTimeout() 是用于 ** 在指定的延迟时间后,一次性执行某段代码(回调函数)** 的定时器,执行完毕后定时器自动失效,不会重复触发,因此也被称为 “一次性定时器” 或 “延迟定时器”。

(2)语法格式

// 基础语法:返回定时器ID(用于后续清除定时器)
const timeoutId = setTimeout(callback, delay, [param1, param2, ...]);
  • 关键参数说明:

    callback:必填,延迟后要执行的回调函数(可以是普通函数、箭头函数,也可以是字符串形式的代码片段,不推荐后者,存在安全风险且性能较差);

    delay:可选,延迟执行的时间,单位为毫秒(ms),默认值为0(注意:不是立即执行,后续会详解);

    param1, param2, ...:可选,传递给回调函数的参数,在 ES5 及以上环境中支持;

    返回值timeoutId:非负整数(定时器唯一标识 ID),每个定时器对应一个唯一 ID,用于后续清除该定时器。

(3)实战示例

// 示例1:基础使用——3秒后一次性执行回调
const timeoutId1 = setTimeout(() => {
  console.log("3秒后执行,仅执行一次");
}, 3000);

console.log("定时器已创建,ID为:", timeoutId1); // 输出:定时器已创建,ID为:1(不同环境ID可能不同)

// 示例2:传递参数给回调函数
const timeoutId2 = setTimeout((name, age) => {
  console.log(`你好,我是${name},今年${age}岁`);
}, 2000, "张三", 25); // 2秒后输出:你好,我是张三,今年25岁

// 示例3:延迟时间为0(非立即执行,进入任务队列等待执行)
setTimeout(() => {
  console.log("延迟0毫秒,最后执行");
}, 0);

console.log("同步代码,先执行");
// 输出顺序:同步代码,先执行 → 延迟0毫秒,最后执行

(4)核心执行机制

setTimeout() 的延迟时间delay并非 “绝对准确”,也不是 “到点立即执行”,核心原因是 JavaScript 的单线程特性:

  • 当调用setTimeout()时,JS 引擎会将回调函数放入宏任务队列,并记录延迟时间;
  • 主线程优先执行完当前所有同步代码,再去处理宏任务队列;
  • 只有当主线程空闲,且延迟时间已到,回调函数才会被执行;
  • 若主线程被其他耗时同步代码阻塞,回调函数的执行时间会大于delay设定的时间。

2. 周期性定时器:setInterval ()

(1)核心定义

setInterval() 是用于 ** 按照指定的时间间隔,周期性、重复执行某段代码(回调函数)** 的定时器,除非手动清除,否则会一直持续执行,因此也被称为 “周期性定时器” 或 “重复定时器”。

(2)语法格式

// 基础语法:返回定时器ID(用于后续清除定时器)
const intervalId = setInterval(callback, delay, [param1, param2, ...]);
  • 关键参数说明(与setTimeout()一致,核心差异在执行逻辑):

    callback:必填,每次间隔时间到后要执行的回调函数;

    delay:可选,两次回调执行之间的时间间隔,单位为毫秒(ms),默认值为0

    param1, param2, ...:可选,传递给回调函数的参数;

    返回值intervalId:非负整数(定时器唯一标识 ID),用于后续清除该周期性定时器。

(3)实战示例

// 示例1:基础使用——每隔2秒重复执行回调
let count = 0;
const intervalId1 = setInterval(() => {
  count++;
  console.log(`已执行${count}次,每隔2秒执行一次`);
}, 2000);

// 示例2:传递参数给回调函数,实现倒计时
let remainingTime = 10;
const intervalId2 = setInterval((title) => {
  remainingTime--;
  console.log(`${title}:剩余${remainingTime}秒`);
  if (remainingTime <= 0) {
    console.log("倒计时结束");
    // 后续会详解:倒计时结束后清除定时器
    clearInterval(intervalId2);
  }
}, 1000, "秒杀活动"); // 每隔1秒输出一次倒计时

(4)核心执行机制

setInterval() 的执行机制与setTimeout() 类似,同样受 JS 单线程和宏任务队列影响,但存在一个关键差异:

  • 首次调用setInterval()时,JS 引擎会将回调函数放入宏任务队列,等待延迟时间delay后执行;
  • 回调函数执行完毕后,setInterval() 会再次将下一次的回调函数放入宏任务队列,保持delay时间间隔的周期性;
  • 若某一次回调函数执行耗时超过delay时间间隔,后续回调会出现 “堆积”,导致执行间隔小于delay(这是setInterval() 的常见坑);
  • 除非手动清除,否则该过程会一直循环,直到页面卸载或定时器被清除。

三、第二部分:两种定时器的核心区别(5 大维度对比)

setTimeout() 和 setInterval() 虽然都是定时器,且语法格式相似,但在执行逻辑、使用场景等方面存在本质区别,通过以下 5 大维度可直观区分:

对比维度一次性定时器(setTimeout ())周期性定时器(setInterval ())
执行次数仅执行一次,回调执行完毕后定时器自动失效重复执行多次,直到手动清除或页面卸载
核心用途实现单次延迟执行(如延迟提示、防抖兜底)实现周期性重复执行(如倒计时、轮询、轮播)
定时器状态执行后自动失效,无需手动清除(除非提前终止)持续处于激活状态,必须手动清除否则一直执行
时间特性延迟delay毫秒后执行单次回调每隔delay毫秒执行一次回调,存在执行堆积风险
底层逻辑单次将回调放入宏任务队列周期性将回调放入宏任务队列,形成循环

补充:关键差异实战验证

// 1. setTimeout():仅执行一次
setTimeout(() => {
  console.log("setTimeout:我只执行一次");
}, 1000);

// 2. setInterval():重复执行,需手动清除
let times = 0;
const intervalId = setInterval(() => {
  times++;
  console.log(`setInterval:我执行了${times}次`);
  if (times === 3) {
    clearInterval(intervalId); // 执行3次后手动清除
    console.log("setInterval:我被清除了,停止执行");
  }
}, 1000);

输出结果顺序

plaintext

setTimeout:我只执行一次
setInterval:我执行了1次
setInterval:我执行了2次
setInterval:我执行了3次
setInterval:我被清除了,停止执行

四、第三部分:如何清除定时器?(核心方法与实战)

无论是setTimeout() 还是 setInterval(),创建时都会返回一个唯一的定时器 ID,清除定时器的核心就是通过这个 ID,调用对应的清除方法,终止定时器的执行(或周期性执行)。

1. 对应清除方法:一对一清除,不可混用

JS 提供了两个专门的定时器清除方法,分别对应两种定时器,需一一对应使用,不可混用(虽然混用在部分浏览器中可能生效,但不符合规范,存在兼容性风险)。

(1)清除一次性定时器:clearTimeout ()

核心功能:用于清除由setTimeout() 创建的一次性定时器,终止回调函数的执行(仅对未执行的定时器有效,若回调已执行,定时器已失效,调用该方法无任何效果)。

语法格式

// timeoutId 是 setTimeout() 返回的定时器ID
clearTimeout(timeoutId);

实战示例

// 1. 创建一次性定时器
const timeoutId = setTimeout(() => {
  console.log("该回调不会被执行,因为定时器被提前清除了");
}, 3000);

console.log("创建定时器,准备2秒后清除它");

// 2. 提前2秒清除定时器(此时回调还未执行)
setTimeout(() => {
  clearTimeout(timeoutId);
  console.log("定时器已被清除,回调终止执行");
}, 2000);

(2)清除周期性定时器:clearInterval ()

核心功能:用于清除由setInterval() 创建的周期性定时器,终止回调函数的后续重复执行(调用后,定时器立即失效,不再向宏任务队列添加新的回调)。

语法格式

// intervalId 是 setInterval() 返回的定时器ID
clearInterval(intervalId);

实战示例

// 1. 创建周期性定时器(实现倒计时)
let countdown = 5;
const intervalId = setInterval(() => {
  countdown--;
  if (countdown > 0) {
    console.log(`倒计时剩余:${countdown}秒`);
  } else {
    console.log("倒计时结束,清除定时器");
    // 2. 倒计时结束,清除周期性定时器
    clearInterval(intervalId);
  }
}, 1000);

输出结果

plaintext

倒计时剩余:4秒
倒计时剩余:3秒
倒计时剩余:2秒
倒计时剩余:1秒
倒计时结束,清除定时器

2. 清除定时器的关键注意事项

(1)必须保存定时器 ID,否则无法精准清除

创建定时器时,一定要将返回的 ID 赋值给变量保存,若丢失 ID,将无法精准清除该定时器,只能等待页面卸载或使用clearTimeout()/clearInterval() 不带参数清除所有定时器(不推荐,会清除其他无关定时器)。

// 错误示例:未保存定时器ID,无法精准清除
setInterval(() => {
  console.log("无法精准清除,只能一直执行到页面卸载");
}, 1000);

// 正确示例:保存定时器ID,便于后续精准清除
const intervalId = setInterval(() => {
  console.log("可通过ID精准清除");
}, 1000);
clearInterval(intervalId);

(2)避免 “定时器泄漏”:组件卸载 / 页面关闭前清除定时器

在前端框架(React、Vue)开发中,若组件中创建了定时器,在组件卸载时未清除,会导致 “定时器泄漏”—— 组件已销毁,但定时器仍在后台执行,不仅浪费内存,还可能引发报错(如访问已销毁的组件 DOM)。

Vue 组件示例(组件卸载前清除定时器)

<template>
  <div>组件倒计时:{{ countdown }}</div>
</template>

<script>
export default {
  data() {
    return {
      countdown: 10,
      intervalId: null // 保存定时器ID
    };
  },
  mounted() {
    // 创建周期性定时器
    this.intervalId = setInterval(() => {
      this.countdown--;
      if (this.countdown <= 0) {
        clearInterval(this.intervalId);
      }
    }, 1000);
  },
  beforeUnmount() {
    // 组件卸载前清除定时器,避免泄漏
    if (this.intervalId) {
      clearInterval(this.intervalId);
    }
  }
};
</script>

(3)延迟时间为 0 的定时器,仍可被清除

即使setTimeout() 的延迟时间设为0,回调函数仍未立即执行(处于宏任务队列等待),此时调用clearTimeout() 仍可终止其执行。

const timeoutId = setTimeout(() => {
  console.log("该回调不会被执行");
}, 0);

clearTimeout(timeoutId); // 立即清除,回调无法执行

(4)清除已失效 / 不存在的定时器,不会报错

若对已执行完毕的setTimeout() 定时器、已清除的setInterval() 定时器,或不存在的 ID 调用清除方法,JS 引擎不会抛出错误,仅会静默失败,无需额外做判断(若需严谨,可先判断 ID 是否存在)。

// 1. 清除已执行完毕的setTimeout()定时器
const timeoutId = setTimeout(() => {
  console.log("回调已执行");
}, 1000);

// 2秒后回调已执行,定时器已失效
setTimeout(() => {
  clearTimeout(timeoutId); // 无报错,静默失败
}, 2000);

// 3. 清除不存在的定时器ID
clearInterval(9999); // 无报错,静默失败

五、第四部分:实战避坑指南 —— 定时器的常见问题与解决方案

1. 坑 1:setInterval () 回调执行堆积,导致间隔不准

问题现象:当setInterval() 的回调函数执行耗时超过delay时间间隔,后续回调会在宏任务队列中堆积,回调执行间隔小于预期的delay

解决方案:使用setTimeout() 嵌套调用,模拟周期性执行,避免堆积(每次回调执行完毕后,再创建下一个定时器,保证间隔准确)。

// 推荐:setTimeout() 嵌套,实现精准间隔的周期性执行
let count = 0;
function periodicExecute() {
  // 1. 执行核心业务逻辑
  count++;
  console.log(`执行第${count}次,间隔2秒`);

  // 2. 回调执行完毕后,创建下一个定时器,保证间隔准确
  if (count < 5) {
    setTimeout(periodicExecute, 2000);
  }
}

// 启动周期性执行
setTimeout(periodicExecute, 2000);

2. 坑 2:混淆 “延迟时间” 与 “执行时间”,预期不符

问题现象:认为setTimeout(fn, 1000) 会在 1 秒后 “绝对准确” 执行,但实际执行时间可能大于 1 秒(受主线程同步代码阻塞影响)。

解决方案

  • 理解 JS 单线程和宏任务队列机制,不依赖定时器的绝对准确时间;
  • 若需高精度定时,可使用requestAnimationFrame()(适合动画场景,刷新率与屏幕同步)或Web Worker(避免主线程阻塞)。

3. 坑 3:清除定时器后,仍执行一次回调

问题现象:清除周期性定时器后,回调函数仍执行了一次,核心原因是:清除时,下一个回调已经被放入宏任务队列,等待执行。

解决方案

  • 清除定时器前,增加状态判断,避免回调执行无效逻辑;
  • 使用setTimeout() 嵌套替代setInterval(),从根源上避免该问题。
let isActive = true; // 状态标记
let count = 0;

const intervalId = setInterval(() => {
  // 增加状态判断,即使回调已入队,也不执行无效逻辑
  if (!isActive) return;
  
  count++;
  console.log(`执行第${count}次`);
}, 1000);

// 2.5秒后清除定时器
setTimeout(() => {
  isActive = false; // 先标记状态为无效
  clearInterval(intervalId); // 再清除定时器
  console.log("定时器已清除");
}, 2500);

六、总结:核心知识点回顾与实战准则

  • 两种定时器的核心定位setTimeout() 是 “单次延迟”,setInterval() 是 “周期性重复”,根据业务场景选择对应工具;
  • 清除定时器的核心:保存定时器 ID,使用clearTimeout()/clearInterval() 一对一清除,组件卸载 / 页面关闭前避免泄漏;
  • 实战准则
    • 优先使用setTimeout() 嵌套实现精准周期性执行,避免setInterval() 堆积问题;
    • 避免依赖定时器的绝对准确时间,理解 JS 单线程执行机制;
    • 保存定时器 ID,精准清除,杜绝 “定时器泄漏”。

定时器作为 JS 异步编程的基础工具,看似简单,实则蕴含着单线程、任务队列的核心思想。掌握两种定时器的区别与清除方法,不仅能解决日常开发中的延迟、重复执行需求,更能为后续深入学习 Promise、async/await 等异步编程技术打下坚实基础。

最后用一句话总结:setTimeout() 是 “一次性的等待”,setInterval() 是 “无休止的循环”,清除定时器是 “及时止损的智慧”,选对、用对、清对,才能驾驭 JS 的异步延迟逻辑

到此这篇关于js中的两种定时器区别是什么以及怎么清除定时器的文章就介绍到这了,更多相关js两种定时器区别及清除内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • JS如何修改数组对象的Key和指定的值

    JS如何修改数组对象的Key和指定的值

    这篇文章主要介绍了JS如何修改数组对象的Key和指定的值,本文通过示例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-10-10
  • js仿360开机效果

    js仿360开机效果

    这篇文章主要为大家详细介绍了js仿360开机效果,并且封装一个带回调函数的缓动动画,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2019-12-12
  • JavaScript最全公共方法汇总并解析(前端开发收藏必备)

    JavaScript最全公共方法汇总并解析(前端开发收藏必备)

    JavaScript掌握各种常用的公共方法更是提升开发效率和代码质量的关键,无论你是初学者还是资深开发者,了解并熟练运用这些方法都能让你的代码更加简洁、高效,本篇博客将为你详细汇总并解析最全的JavaScript公共方法,涵盖数组、对象、字符串、日期等各个方面的常用技巧
    2024-06-06
  • 浅谈JS中this在各个场景下的指向

    浅谈JS中this在各个场景下的指向

    这篇文章主要介绍了浅谈JS中this在各个场景下的指向,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-08-08
  • JS常用的几种数组遍历方式以及性能分析对比实例详解

    JS常用的几种数组遍历方式以及性能分析对比实例详解

    这篇文章主要介绍了JS常用的几种数组遍历方式以及性能分析对比,结合实例形式详细分析了javascript针对数组遍历的几种常见使用方法及执行效率对比,具有一定参考借鉴价值,需要的朋友可以参考下
    2018-04-04
  • Javascript类定义语法,私有成员、受保护成员、静态成员等介绍

    Javascript类定义语法,私有成员、受保护成员、静态成员等介绍

    JS只是一门支持面向对象编程的语言,通过OO可以让我们的代码组织更加人性化。可是与传统基与类的面向对编程语言不同它没有类概念并且没成员访问修饰符。这多少会给我们编程工作会带来一些束缚
    2011-12-12
  • JS字符串截取出现的bug以及解决方式

    JS字符串截取出现的bug以及解决方式

    之前在获取元素属性时,踩了个坑,记录一下,下面这篇文章主要给大家介绍了关于JS字符串截取出现的bug以及解决方式,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-12-12
  • JS使用Promise控制请求并发数

    JS使用Promise控制请求并发数

    现在面试过程当中 ,手写题必然是少不了的,其中碰到比较多的无非就是当属 请求并发控制了,所以本文为大家整理了JS使用Promise控制请求并发数的示例代码,希望对大家有所帮助
    2023-05-05
  • 手写TypeScript 时很多人常犯的几个错误

    手写TypeScript 时很多人常犯的几个错误

    这篇文章主要介绍了手写TypeScript 时很多人常犯的几个错误,文章围绕主题展开详细的内容介绍,具有一定的抽卡,重要的朋友可以参考一下
    2022-09-09
  • javascript实现简单放大镜效果

    javascript实现简单放大镜效果

    这篇文章主要为大家详细介绍了javascript实现简单放大镜效果,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2021-09-09

最新评论