Skip to content
大纲

计时相关问题

使用闭包实现每隔一秒打印 1,2,3

js
// code1
const counter = (max) => {
  for (let i = 1; i <= max; i++) {
    setTimeout(console.log, i * 1000, i)
  }
}
counter(3)

如果要求使用 Promise 实现,且要循环输出呢?

答案
js
const counter = (max) => {
  const arr = Array.from({ length: max }, (x, i) => i)
  arr.reduce((p, item) => {
    return p.then(() => {
      return new Promise((resolve) => {
        setTimeout(() => resolve(console.log(item)), 1000)
      })
    })
  }, Promise.resolve())
}

counter(3)

使用 setTimeout 模拟实现 setInterval

js
// 精简版本
function mySetInterval(handler, timeout = 0) {
  setTimeout(() => {
    arguments[0]()
    mySetInterval(...arguments)
  }, arguments[1] || 0)
}
js
// 支持清除定制器功能
function mySetInterval(handler, timeout = 0) {
  let timer = {
    value: 0,
    valueOf: function () {
      return this.value
    }
  }
  function myTimer(...rest) {
    timer.value = setTimeout(() => {
      rest[0]()
      myTimer(...rest)
    }, rest[1])
  }
  myTimer(...arguments)
  return timer
}

mySetInterval.clear = (timer) => {
  clearTimeout(timer)
}

// testing
const timer1 = mySetInterval(() => {
  console.log(111)
}, 1000)
const timer2 = mySetInterval(() => {
  console.log(222)
}, 1100)

setTimeout(() => {
  mySetInterval.clear(timer1)
}, 5000)
setTimeout(() => {
  mySetInterval.clear(timer2)
}, 9000)
js
function mySetInterval(fn, timeout) {
  // 控制器,控制定时器是否继续执行
  var timer = {
    flag: true
  }
  // 设置递归函数,模拟定时器执行。
  function interval() {
    if (timer.flag) {
      fn()
      setTimeout(interval, timeout)
    }
  }
  // 启动定时器
  setTimeout(interval, timeout)
  // 返回控制器
  return timer
}

// testing
const timer1 = mySetInterval(() => {
  console.log(111)
}, 1000)
const timer2 = mySetInterval(() => {
  console.log(222)
}, 1000)

setTimeout(() => {
  timer1.flag = false
}, 5000)
setTimeout(() => {
  timer2.flag = false
}, 9000)

浏览器中 setInterval 定义

long setInterval(in any handler, in optional any timeout, in any... args)

  1. 该方法返回的句柄是不变的 long 值(通过该句柄去取消定时器)
  2. 该方法的执行上下文必须是 window, WorkerUtils

格式:mySetInterval(handler, ?timeout, ...args)

思考

为什么要用 setTimeout 模拟实现 setInterval?

setInterval 的缺陷是什么?

扩展:我们能反过来使用 setinterval 模拟实现 settimeout 吗?

点我查看详细
js
const mySetTimeout = (fn, time) => {
  const timer = setInterval(() => {
    clearInterval(timer)
    fn()
  }, time)
}

如何实现一个 0ms 的 setTimeout?

setTimeout 为什么最小只能设置 4ms,如何实现一个 0ms 的 setTimeout?

点我查看详细
js
let timeouts = []
const messageName = 'zero-settimeout'

function setTimeoutZero(fn) {
  timeouts.push(fn)
  window.postMessage(messageName, '*')
}

function handleMessage(evt) {
  if (evt.source == window && evt.data === messageName) {
    if (timeouts.length > 0) {
      const f = timeouts.shift()
      f()
    }
  }
}

window.addEventListener('message', handleMessage)

window.zeroSettimeout = setTimeoutZero