亮神知识库 亮神知识库
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)

亮神

前程明亮,未来可期
首页
  • 手写代码

    • 手写代码系列
  • 基础知识

    • 基础
    • JS底层
    • CSS
  • 原理
  • 浏览器
  • HTTP
  • 网络安全
  • babel
  • webpack基础
  • webpack进阶
  • Vite
  • TypeScript
  • Vue2
  • Vue3
  • Node基础

    • glob
    • 模块化机制
    • 事件循环
    • KOA2框架原理
    • Node子进程
    • cluster原理(了解)
  • 教育行业2021

    • B端
    • C端
    • 工具
  • 游戏行业2025
  • 刷题
  • 杂(待整理)
  • 学习
  • 面试
  • 实用技巧
  • 心情杂货
  • 年度总结
  • 友情链接
关于
  • 分类
  • 标签
  • 归档
  • 收藏
GitHub (opens new window)
  • 基础

  • 手写代码

    • 手写类型转换
    • 手写累加、累乘函数
    • 手写new
    • 手写深拷贝
    • 手写Object.create
    • 手写继承
    • 手写extends
    • 手写instanceof
    • 手写call、apply、bind
    • 手写jsonp
    • 手写getQueryString
    • 手写setInterval
      • setTimout、setInterval
      • 为什么setTimeout不准确
      • requestAnimationFrame
    • 手写防抖与节流
    • 手写对象属性值迭代器
    • 手写分时函数
    • 手写事件委托
    • 手写图片懒加载
    • 手写原生Ajax请求
    • 手写AOP装饰函数
    • 手写柯里函数
    • 手写数组扁平化flat
    • 手写数组去重
    • 手写eventEmit类
    • 手写Vue数据响应式
    • 手写Vue nextTick
    • 手写Promise
  • JS底层深入

  • CSS

  • 基础
  • 手写代码
0zcl
2021-06-18
目录

手写setInterval

# setTimout、setInterval

  • setTimeout()
  1. 返回值timeoutID是一个正整数,表示定时器的编号
  2. 如果 setTimeout 存在嵌套调用,那么系统会设置最短时间间隔为 4 毫秒
console.log('begin', new Date().getTime())
setTimeout(()=> {
  setTimeout(()=> {
    console.log('end', new Date().getTime())
  }, 0)
}, 0)
// begin: 1625994293493
// end: 1625994293497
1
2
3
4
5
6
7
8

# 为什么setTimeout不准确

setTimeout设置了一个定时器,表示在指定时间之后才放入事件循环队队(宏任务队列),如果此时队列中已经有其它任务,依据队列<code>先入先出原则</code>,要等前面的任务都执行完,才会执行定时器的任务。因此,setTimeout定时器任务设置的时间往往不是一个准确的时间

  • setInterval()
  1. setInterval的最短间隔时间是10毫秒,也就是说,小于10毫秒的时间间隔会被调整到10毫秒
  2. javascript引擎:当使用setInterval时,如果队列中存在之前由其添加的回调函数,就会放弃本次的添加

根据第2点的原则,会导致两个问题:

  1. 某些间隔被跳过. 即setInterval任务被跳过
  2. 多个定时器的代码执行之间的间隔可能比预期的小
btn.onclick = function(){
    setTimeout(function(){
        console.log(1);
    },250);
}
1
2
3
4
5

setInterval

在205ms处把interval定时器任务添加到任务队列,此时需排队执行,在300ms时执行定时器任务。当执行这个定时器代码时,在405ms处又给队列添加了另一个副本。在下一个间隔,即605ms处,第一个定时器仍在运行,同时队列中已经存在一个定时器任务了。故605ms处,定时器任务会跳过。

::: info 405ms处,定时器还能添加到队列,是因为此时第一个定时器任务已经出队列,在执行栈中执行了,此时队列并无定时器任务,故能添加。 :::

  • requestAnimationFrame() 大多数电脑显示器的刷新频率是60Hz,大概相当于每秒钟重绘60次。大多数浏览器都会对重绘操作加以限制,不超过显示器的重绘频率,因为即使超过那个频率用户体验也不会有提升。因此,最平滑动画的最佳循环间隔是1000ms/60,约等于16.6ms

setTimeout和setInterval的问题是,它们都不精确。它们的内在运行机制决定了时间间隔参数实际上只是指定了把动画代码添加到浏览器UI线程队列中以等待执行的时间。如果队列前面已经加入了其他任务,那动画代码就要等前面的任务完成后再执行

<code>requestAnimationFrame</code>采用系统时间间隔,<code>保持最佳绘制效率</code>,不会因为间隔时间过短,造成过度绘制,增加开销;也不会因为间隔时间太长,使用动画卡顿不流畅,让各种网页动画效果能够有一个统一的刷新机制,从而节省系统资源,提高系统性能,改善视觉效果

# requestAnimationFrame

注意

用requestAnimationFrame实现自己的setInterval方法

答:

var obj = {
  timer: null,
  setInterval: (callback, interval) => {
    let startTime = new Date().getTime()
    const loop = () => {
      console.log('loop')
      const endTime = new Date().getTime()
      this.timer = requestAnimationFrame(loop)
      if (endTime - startTime > interval) {
        callback && callback()
        startTime = new Date().getTime()
      }
    }
    this.timer = requestAnimationFrame(loop)
    return this.timer
  },
  clearInterval: () => {
    cancelAnimationFrame(this.timer)
  }
}

// 测试
var count = 0
var timer = obj.setInterval(() => {
  count++
  console.log('count', count)
  if (count > 3) {
    obj.clearInterval()
  }
}, 1000)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30

<code>window.requestAnimationFrame()</code>传入一个回调函数作为参数,该回调函数会在浏览器下一次重绘之前执行。

提示

window.requestAnimationFrame只会调用一次回调。若你想在浏览器下次重绘之前继续更新下一帧动画,那么回调函数自身必须再次调用window.requestAnimationFrame()

参考:深入理解定时器系列第一篇——理解setTimeout和setInterval (opens new window) window.requestAnimationFrame (opens new window)

编辑 (opens new window)
上次更新: 2025/07/20, 06:21:22
手写getQueryString
手写防抖与节流

← 手写getQueryString 手写防抖与节流→

最近更新
01
2024年
07-20
02
2023年
07-20
03
2022年
07-20
更多文章>
Theme by Vdoing | Copyright © 2025-2025 亮神 | MIT License | 桂ICP备2024034950号 | 桂公网安备45142202000030
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式