阅读 115

JavaScript 计时器

计时器

  • var id = setTimeout(fn, delay):开始一个timer,将在delay时间后调用fn
  • var id = setInterval(fn, delay)
  • clearInterval(id), clearTimeout(id)

计时器执行时间

计时器的延迟是没有保证的。

由于浏览器中的 JS 都在单线程里执行的,因此只有在执行过程中空闲时才会执行异步事件(包括鼠标点击和计时器)。

最直观的例子:

setTimeout(function() {
  console.log(1);
}, 0);
console.log(2);
复制代码

输出结果是 2 1。上例中看似在 0ms 后打印 1,但事实上是在执行完同步的 JS 后才执行计时器内的代码。

异步队列

异步事件(包括计时器、鼠标事件、XMLHttpRequest完成等)会被加入一个队列等待执行,队列的实现因浏览器而异。

image

在同步代码执行时,把所有异步事件加入队列中。当同步代码执行完后(18ms),先执行鼠标点击事件(10ms),然后执行计时器,这时已经过了 28ms。

Intervals 不关心当前执行的是什么,他们会无差别地添加到队列中。图中的 interval 注册是 10ms 间隔,但是在执行过程中因为 interval 回调函数本身的执行时间大于或等于 10ms,就会牺牲掉 interval 每个回调函数之间的间隔。

setTimeout 和 setInterval

setTimeout(function(){
  // ...
  setTimeout(arguments.callee, 10);
}, 10);
 
setInterval(function(){
  // ...
}, 10);
复制代码

上面的两种写法看起来结果是一样的,但是实际上有差别。由于 setTimeout 的写法是每次在回调函数执行时添加一个新的计时器,所以这个回调每次执行的间隔至少是 10ms。而 setInterval 的写法使回调每 10ms 执行一次。

要点总结

  • JavaScript 单线程使得异步事件要排队等待执行。
  • setTimeoutsetInterval 本质上不同。
  • setInterval 注册的间隔是回调开始执行的间隔,如果回调执行超过间隔时间,就会连续无延迟地执行。

参考

https://johnresig.com/blog/how-javascript-timers-work/

文章分类
前端
文章标签