JS忍者秘籍中的定时器机制详解

简介: 前言前段时间刚看完《JS忍者秘籍》,虽说是15年出版的,有些东西是过时了,但像对原型链、闭包、正则、定时器之类的机制却是不会过时的,里面很多东西都讲的很细,还是值得一读的,本文将对这本书中对定时器机制的部分进行详细的解析,如果喜欢的话可以点波赞/关注,支持一下,希望大家看完本文可以有所收获。个人博客了解一下:obkoro1.com准备阅读本文之前,推荐先阅读Js 的事件循环(Event Loop)机制以及实例讲解这篇文章来理解背后发生的事情,本文对事件循环机制不会很仔细的讲解。定时器解决的问题:由于JS的单线程特性,定时器提供了一种跳出单线程限制的方法。

image.png

前言

前段时间刚看完《JS忍者秘籍》,虽说是15年出版的,有些东西是过时了,但像对原型链、闭包、正则、定时器之类的机制却是不会过时的,里面很多东西都讲的很细,还是值得一读的,本文将对这本书中对定时器机制的部分进行详细的解析,如果喜欢的话可以点波赞/关注,支持一下,希望大家看完本文可以有所收获。


准备

阅读本文之前,推荐先阅读Js 的事件循环(Event Loop)机制以及实例讲解这篇文章来理解背后发生的事情,本文对事件循环机制不会很仔细的讲解。

定时器解决的问题:

由于JS的单线程特性,定时器提供了一种跳出单线程限制的方法,即让一段代码在一定毫秒之后,再异步执行。

设置和清除定时器:

直接引用忍者秘籍中的图片:

image.png

注意:

  1. 定时器的时间间隔设为0,也会有几毫秒的延迟。
  2. 在使用setTimeoutsetInterval的时候最好将其赋值给一个变量,以便取消定时器。
  3. 在使用Vue的时候,setTimeoutsetInterval的this指向的是window对象,访问不到组件数据以及方法。
  4. 在使用Vue的时候,路由跳转并不会销毁setInterval,但是组件已经销毁了,这会导致问题。
  5. 在执行线程中setTimeout/setInterval无法保证准时执行回调函数的。
  6. setInterval调用有可能会被废弃以及setInterval的连续执行

第三点和第四点的解决方法可以参考我之前写的Vue 实践过程中的几个问题

接下来要讲的是第五点和第六点,这两点是最重要,也是本文要重点解析的内容。

执行线程中的定时器执行

下面来看忍者秘籍中的栗子:

image.png

让我们看看这里发生了什么事情:

  1. 首先在0毫秒的时候有一个持续18毫秒的js代码块要执行。
  2. 然后在0毫秒的时候设了两个10毫秒延迟的定时器,setTimeout以及setInterval,setTimeout先设定。
  3. 在第6毫秒的时候有一个发生了鼠标单击事件。

事件排队。

同时发生了这么多事情,由于js的单线程特性,当线程正在执行状态,有异步事件触发时,它就会排队,并且在线程空闲时才进行执行

这里的异步事件包括:鼠标单击,定时器触发,ajax请求、promise等事件。

让我们回到栗子中:

栗子中首先有一个18毫秒的代码块要执行,在这18毫秒中只能执行这段代码块,其他事件触发了之后只能排队等待执行

在代码块还在运行期间,第6毫秒的时候,发生了一个鼠标单击事件,以及第10毫秒时的setTimeoutsetInterval两个处理程序,这三个事件不能立即执行,而是被添加到等待执行的队列中。

先进先出(先排队的先执行)

18毫秒的时候代码块结束执行,有三个任务在排队等待执行,根据先进先出的原则,此时会先执行click事件setTimeoutsetInterval将继续排队等待执行。

setInterval调用被废弃

在click事件执行时,第20毫秒处,第二个setInterval也到期了,因为此时已经click事件占用了线程,所以setInterval还是不能被执行,并且因为此时队列中已经有一个setInterval正在排队等待执行,所以这一次的setInterval的调用将被废弃

浏览器不会对同一个setInterval处理程序多次添加到待执行队列。

setTimeout/setInterval无法保证准时执行回调函数

click事件在第28毫秒处结束执行,有两个任务(setTimeoutsetInterval)正在等待执行,遵循先进先出的原则,setTimeout早于setInterval设定,所以先执行setTimeout

so:我们期望在第10毫秒处执行的setTimeout处理程序,最终会在第28毫秒处才开始执行,这就是上文提到的setTimeout/setInterval无法保证准时执行回调函数。

在30毫秒处,setInterval又触发了,因为队列中已经有setInterval在排队,所以这次的触发又作废了。

setInterval的连续执行

setTimeout执行结束,在第36毫秒处,队列中的setInterval处理程序才开始执行,setInterval需要执行6毫秒。

在第40毫秒的时候setInterval再次触发,因为此时上一个setInterval正在执行期间,队列中并没有setInterval在排队,这次触发的setInterval将进入队列等候

所以:setInterval的处理时长不能比设定的间隔长,否则setInterval将会没有间隔的重复执行

第42毫秒的时候,第一个setInterval结束,然后队列中的setInterval立即开始执行,在48毫秒的时候完成执行。然后50毫秒的时候再次触发setInterval,此时没有任务在排队,将会立即执行。

setTimeout按照一定的间隔周期性的触发定时器。

上文说了,setInterval的处理时长不能比设定的间隔长,否则setInterval将会没有间隔的重复执行

但是对这个问题,很多情况下,我们并不能清晰的把控处理程序所消耗的时长,为了我们能按照一定的间隔周期性的触发定时器,忍者秘籍中提供了下面这种使用方法:

// 实际上我不止在忍者秘籍中见过,在很多地方都见过这种技术。
    setTimeout(function repeatMe(){
      // do something
      setTimeout(repeatMe,10); 
      // 执行完处理程序的内容后,在末尾再间隔10毫秒来调用该程序,这样就能保证一定是10毫秒的周期调用
    },10)

忍者秘籍中关于定时器的其他知识:

  • 定时器不能非常细粒化的控制执行的时间,书中建议在15ms以上。
  • 可以使用定时器来分解长时间运行的任务,这里可以自行谷歌。

任务队列只有排队这么简单吗?

事实上,关于任务队列并不是只有简单的排队而已,忍者秘籍中提到为了方便,使用了这个概念,如果希望能更清晰的了解背后的机制,再次推荐阅读一下:Js 的事件循环(Event Loop)机制以及实例讲解


结语

这上面所有一切,都是由js单线程特性导致的,所以会有事件排队、先进先出、setInterval调用被废弃、定时器无法保证准时执行回调函数以及出现setInterval的连续执行,时刻记住这一特性,很多关于事件执行顺序的问题都能想的通,并且找出解决方法。


目录
相关文章
|
28天前
|
JavaScript 前端开发 算法
js 内存回收机制
【8月更文挑战第23天】js 内存回收机制
30 3
|
28天前
|
存储 JavaScript 前端开发
学习JavaScript 内存机制
【8月更文挑战第23天】学习JavaScript 内存机制
22 3
|
1月前
|
JavaScript 前端开发
解决在JS中阻止定时器“重复”开启问题、Vue中定时器的使用
这篇文章讨论了在JavaScript和Vue中如何避免定时器重复开启的问题,并提供了一个模拟场景和代码示例,演示了在开启新定时器前清除旧定时器的方法。
解决在JS中阻止定时器“重复”开启问题、Vue中定时器的使用
|
20天前
|
JavaScript 中间件 开发者
深入浅出Node.js中间件机制
【8月更文挑战第31天】本文将带你领略Node.js中间件的奥秘,通过直观的案例分析,揭示其背后的设计哲学。你将学会如何运用中间件构建强大而灵活的后端应用,以及在面对复杂业务逻辑时如何保持代码的清晰与高效。
|
20天前
|
设计模式 JavaScript 中间件
深入浅出Node.js中间件机制
【8月更文挑战第31天】在Node.js的世界里,中间件如同魔法般存在,它让复杂的请求处理变得井然有序。本文将带你领略中间件的奥秘,从原理到实战,一步步揭开它的神秘面纱。你将学会如何运用中间件来构建强大而灵活的后端应用,就像拼乐高一样有趣。
|
1月前
|
JavaScript 前端开发
js之永久定时器
js之永久定时器
|
1月前
|
JavaScript 前端开发
|
1月前
|
Web App开发 JavaScript 前端开发
JavaScript——定时器为什么是不精确的
JavaScript——定时器为什么是不精确的
33 0
|
1月前
|
JavaScript 前端开发
js之永久定时器
js之永久定时器
23 0
|
1月前
|
JavaScript 前端开发
js之一次性定时器
js之一次性定时器
23 0