用于实现计划调用

setTimeout

将函数推迟到一段时间间隔之后再执行
let timerId = setTimeout(func|code, [delay], [arg1], [arg2], ...)

func|code
想要执行的函数或代码字符串。 一般传入的都是函数。由于某些历史原因,支持传入代码字符串,但是不建议这样做
delay
执行前的延时,以毫秒为单位,(默认值是 0,立即在下一个事件循环执行)
arg1,arg2...
要传入被执行函数的参数列表

取消

clearTimeout(timerId)

setInterval

重复运行一个函数,从一段时间间隔之后开始运行,之后以该时间间隔连续重复运行该函数
语法同 setTimeout

取消

clearInterval(timerId)

周期性调用

有 2 种方式:

  • setInterval
  • 嵌套的 setTimeout

setInterval 有一个问题:那就是函数的执行本身需要花费时间,而定时器是会忽略这段时间,一直以固定的时间间隔运行。就会导致两次函数执行的具体间隔少于设置的时间

用嵌套的 setTimeout 可以解决这个问题,它会在每次函数执行完成后再开始下一次的计时:

let timerId = setTimeout(function tick() { // 命名内部函数
  alert('tick')
  timerId = setTimeout(tick, 2000)
}, 2000)

特性

setInterval/setTimeout 为它里面的函数调用固定设置了 this = window

let user = {
  userName: "user",
  sayHi() {
    console.log(`Hello, ${this.userName}!`)
  }
}
 
setTimeout(user.sayHi, 1000) // Hello, undefined!
// 隐式丢失,因为可以看作
// let f = user.sayHi
// setTimeout(f, 1000)

这里因为 隐式丢失,同时 this=window,又由于 window 上没有该值所以是 undefined

解决方法

  1. 包装器
setTimeout(function() {
  user.sayHi() // Hello, user!
}, 1000)
  1. 箭头函数
setTimeout(() => user.sayHi(), 1000) // Hello, user!
  1. bind
let newFn = user.sayHi.bind(user)
setTimeout(newFn, 1000) // Hello, user!

其他

NOTE

setInterval/setTimeout 引用的函数不会被垃圾回收
经过 5 重嵌套定时器之后,时间间隔被强制设定为至少 4 毫秒