# 定时器

WARNING

  setInterval 函数的用法与 setTimeout 完全一致,区别仅仅在于 setInterval 指定某个任务每隔一段时间就执行一次,也就是无限次的定时执行。

  所以,就我就自己以 setTimout 为例即可

  某个函数或某段代码,在多少毫秒(默认为 0)后执行。除此之外,还会返回一个整数(表示定时器编号),可以用来取消定时器

var timerId = setTimeout(func|code, delay);

# 1、常规使用

  • 使用
// 1、某段代码
setTimeout('console.log(2)', 1000)

// 2、某个函数
setTimeout(function () {
  console.log(2)
}, 1000)
  • 取消
var id1 = setTimeout(f, 1000)
var id2 = setInterval(f, 1000)

clearTimeout(id1)
clearInterval(id2)

# 2、更多内容

# ① 传参

setTimeout(
  function (a, b) {
    console.log(a + b)
  },
  1000,
  1,
  1
)

# ② 执行顺序 ✨

  根据其运行机制:setTimeoutsetInterval 指定的回调函数,必须等到本轮事件循环的所有同步任务都执行完,才会开始执行。

知识回顾

  DOM 事件流:事件冒泡机制?

  我们知道用户自定义的回调函数,通常在浏览器的默认动作之前触发

  例如:用户在输入框输入文本,keypress 事件会在浏览器接收文本之前触发,但我们想要的是 keypress 事件处理的是浏览器接收的文本。

document.getElementById('input-box').onkeypress = function () {
  var self = this
  // 推迟即可
  setTimeout(function () {
    self.value = self.value.toUpperCase()
  }, 0)
}

  又例如:我们可以将那些计算量大、耗时长的任务,放到setTimeout(f, 0)里面执行。

var timer
var i = 0x100000

function func() {
  timer = setTimeout(func, 0)
  div.style.backgroundColor = '#' + i.toString(16)
  if (i++ == 0xffffff) clearTimeout(timer)
}

timer = setTimeout(func, 0)

# ③ this 指向

  我们知道 setTimeout 使得方法内部的 this 关键字指向全局环境,而不是定义时所在的那个对象。

  • 闭包解决
var x = 1

var obj = {
  x: 2,
  y: function () {
    console.log(this.x)
  }
}

setTimeout(function () {
  // 函数套函数✨
  obj.y()
}, 1000)
// 2
  • 函数方法解决
var x = 1

var obj = {
  x: 2,
  y: function () {
    console.log(this.x)
  }
}

// 直接手动改变this指向✨
setTimeout(obj.y.bind(obj), 1000)
// 2

# debounce / throttle 函数

参考 1 (opens new window)
参考 2 (opens new window)

  本质上来讲,使用防抖、节流都与一个目标相关:当多次执行某一动作,进行函数调用次数的限制,节省资源

# 一、防抖

  在事件触发 n 秒后执行函数,如果在 n 秒内再次出发,重新计时

理解

  王者荣耀英雄回城效果

// 使用示例
window.onscroll = function () {
  let testThrottleFn = debounce(function () {
    console.log('发起ajax请求')
  }, 3000)

  testThrottleFn()
}

# 应用场景

  • 登录、发短信等按钮避免用户点击太快,以致于发送了多次请求

  • 在搜索框搜索输入。只需用户最后一次输入完,再发送请求

  • 手机号、邮箱验证输入检测(change、input、blur、keyup 等事件触发,每次键入都会触发)

  • 窗口大小 Resize。只需窗口调整完成后,计算窗口大小。防止重复渲染。

  • 鼠标的 mousemove、mouseover 导航条上,用户不停的在导航区域滑动相当于

# 1、简单版本

var timer //全局的timer,只有一个
function debounce(fn, delay) {
  if (timer) {
    clearTimeout(timer) //保证只开启一个定时器
  }
  timer = setTimeout(function () {
    fn() //延迟delay,执行函数
  }, delay)
}

# 2、闭包版本

function debounce(fn, wait) {
  let timeout

  return function (...arguments) {
    let context = this
    let args = arguments

    if (timeout) {
      clearTimeout(timeout)
    }
    timeout = setTimeout(function () {
      fn.apply(context, args)
    }, wait)
  }
}

# 3、立即执行版本











 
 
 
 
 
 
 
 








function debounce(func, wait, immediate) {
  let timeout

  return function (...arguments) {
    let context = this
    let args = arguments

    if (timeout) {
      clearTimeout(timeout)
    }
    if (immediate) {
      let callNow = !timeout // 第一次会立即执行,以后只有事件执行后才会再次触发
      timeout = setTimeout(function () {
        timeout = null
      }, wait)
      if (callNow) {
        func.apply(context, args)
      }
    } else {
      timeout = setTimeout(function () {
        func.apply(context, args)
      }, wait)
    }
  }
}

# 二、节流

  当多次执行某一动作,每隔一段时间,只执行一次函数。

理解

  王者荣耀英雄平 a 效果

# 应用场景

  • 滚动加载,加载更多或滚到底部监听,window.onscroll 和滑到底部自动加载更多

  • 谷歌搜索框,搜索联想功能

# 1、定时器版本

// 细节1:默认延迟时间设置
function throttle(fn, delay = 500) {
  let timer = null
  return function (...args) {
    if (timer) {
      return
    }
    // 细节2:使用箭头函数(this问题)
    // 指定时间后触发
    timer = setTimeout(() => {
      fn.apply(this, args)
      timer = null
    }, delay)
  }
}

window.onscroll = function () {
  let testThrottleFn = throttle(function () {
    console.log('发起ajax请求')
  }, 3000)

  testThrottleFn()
}

# 2、时间戳版本

function throttle(fn, delay) {
  let oldTime = Date.now()
  return function (...args) {
    let newTime = Date.now()

    // 比较时间间隔
    if (newTime - oldTime >= delay) {
      fn.apply(null, args)
      oldTime = Date.now()
    }
  }
}

window.onscroll = function () {
  let testThrottleFn = throttle(function () {
    console.log('发起ajax请求')
  }, 3000)

  testThrottleFn()
}

# 3、高级写法

function throttled(fn, delay) {
  let timer = null
  let starttime = Date.now()
  return function () {
    let curTime = Date.now() // 当前时间
    let remaining = delay - (curTime - starttime) // 从上一次到现在,还剩下多少多余时间
    let context = this
    let args = arguments
    clearTimeout(timer)
    if (remaining <= 0) {
      fn.apply(context, args)
      starttime = Date.now()
    } else {
      timer = setTimeout(fn, remaining)
    }
  }
}
更新于 : 8/7/2024, 2:16:31 PM