定时器,用来指定某个函数在多少毫秒之后执行。setTimeout(code/function, milliseconds, param1, param2, ...)。返回一个整数,表示定时器的编号,可以通过该编号来取消这个定时器。

  • 为了支持定时器的实现,浏览器增加了延时队列(事件添加在消息队列, 按照消息队列顺序执行)
  • 由于消息队列排队和一些系统级别的限制,setTimeout 没有被实时执行
    • 当前任务执行过久,影响定时器任务的执行
  • 定时器注意事项

    • setTimeout存在嵌套调用, 系统允许设置最短时间间隔为4ms
    • 未激活页面,最小时间间隔为1000ms
    • 延时执行的最大值不超过(2 ** 31 约= 24.8天, 32bit,1bit表示正负), 否则会溢出,相当于设置0ms
    • 回调函数this指向问题

      实现原理

      当通过 JavaScript 调用 setTimeout 设置回调函数的时候:
  • 渲染进程将会创建一个回调任务,包含了回调函数 showName、当前发起时间、延迟执行时间

  • 将该任务添加到延时执行队列 DelayedIncomingQueue delayed_incoming_queue;

处理完消息队列中的一个任务之后,就开始执行 ProcessDelayTask 函数(专门用来处理延迟执行任务)。ProcessDelayTask 函数会根据发起时间和延迟时间计算出到期的任务,然后依次执行这些到期的任务。等到期的任务执行完成之后,再继续下一个循环过程。
clearTimeout函数,浏览器内部通过id查到对应任务,从延时队列删除。

什么情况下没有实时执行?

  1. function bar() {
  2. console.log('bar')
  3. }
  4. function foo() {
  5. setTimeout(bar, 0);
  6. for (let i = 0; i < 5000; i++) {
  7. let i = 5+8+8+8
  8. console.log(i)
  9. }
  10. }
  11. foo() // 耗时500ms

短时间间隔为4ms

执行结果:嵌套调用超过五次以上,后面每次的调用最小时间间隔是 4 毫秒。
原因:在 Chrome 中,定时器被嵌套调用 5 次以上,系统会判断该函数方法被阻塞了,如果定时器的调用时间间隔小于 4 毫秒,那么浏览器会将每次调用的时间间隔设置为 4 毫秒。
解决:requestAnimationFrame 代替 setTimeout 实现的动画

最小时间间隔为1000ms

如果标签不是当前的激活标签,那么定时器最小的时间间隔是 1000 毫秒,目的是为了优化后台页面的加载损耗以及降低耗电量
改变this指向

  1. 箭头函数
  2. bind。setTimeout(MyObj.showName.bind(MyObj), 1000)

    扩展:

  • requestAnimationFrame 实现的动画效果比 setTimeout 好?

使用 requestAnimationFrame 不需要设置具体的时间,由系统来决定回调函数的执行时间,requestAnimationFrame 里面的回调函数是在页面刷新之前执行,它跟着屏幕的刷新频率走,保证每个刷新间隔只执行一次,内如果页面未激活的话,requestAnimationFrame 也会停止渲染,这样既可以保证页面的流畅性,又能节省主线程执行函数的开销

实战:

实现一个打点计时器

  1. /*
  2. 1.从start至end,每隔100毫秒console.log一个数字,每次数字增幅为1
  3. 2.返回的对象中需要包含一个cancel方法,用于停止定时操作
  4. 3.第一个数字需要立即输出
  5. */