资讯专栏INFORMATION COLUMN

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

keelii / 3016人阅读

摘要:设置和清除定时器直接引用忍者秘籍中的图片注意定时器的时间间隔设为,也会有几毫秒的延迟。以上参考资料忍者秘籍第章驯服线程和定时器

前言

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

个人博客了解一下:obkoro1.com
准备

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

定时器解决的问题:

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

设置和清除定时器:

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

注意:

定时器的时间间隔设为0,也会有几毫秒的延迟。

在使用setTimeoutsetInterval的时候最好将其赋值给一个变量,以便取消定时器。

在使用Vue的时候,setTimeoutsetInterval的this指向的是window对象,访问不到组件数据以及方法。

在使用Vue的时候,路由跳转并不会销毁setInterval,但是组件已经销毁了,这会导致问题。

在执行线程中setTimeout/setInterval无法保证准时执行回调函数的。

setInterval调用有可能会被废弃以及setInterval的连续执行

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

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

执行线程中的定时器执行

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

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

首先在0毫秒的时候有一个持续18毫秒的js代码块要执行。

然后在0毫秒的时候设了两个10毫秒延迟的定时器,setTimeout以及setInterval,setTimeout先设定。

在第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的连续执行,时刻记住这一特性,很多关于事件执行顺序的问题都能想的通,并且找出解决方法。

希望看完的朋友可以点个喜欢/关注,您的支持是对我最大的鼓励。

个人blog and 掘金个人主页,如需转载,请放上原文链接并署名。码字不易,感谢支持!本人写文章本着交流记录的心态,写的不好之处,不撕逼,但是欢迎指点。

如果喜欢本文的话,欢迎关注我的订阅号,漫漫技术路,期待未来共同学习成长。

以上2018.6.17

参考资料:

JS忍者秘籍第8章:驯服线程和定时器

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95598.html

相关文章

  • 闭包:私有化变量 《JavaScript高程3》 《JavaScript忍者秘籍

    摘要:闭包闭包的特点就是内部匿名函数可以访问外部函数作用域的变量和方法变量对象。闭包的主要表现形式就是匿名函数,但是两者并不是等价的。中是没有块级作用域的,为了在中引入块级作用域,可以使用匿名函数模拟块级作用域。 在介绍闭包之前,首先解释在随后的测试实例中会使用的assert测试函数,这个方法有别于alert()测试,有很大的改进。 assert()测试方法 #...

    selfimpr 评论0 收藏0
  • 闭包:私有化变量 《JavaScript高程3》 《JavaScript忍者秘籍

    摘要:闭包闭包的特点就是内部匿名函数可以访问外部函数作用域的变量和方法变量对象。闭包的主要表现形式就是匿名函数,但是两者并不是等价的。中是没有块级作用域的,为了在中引入块级作用域,可以使用匿名函数模拟块级作用域。 在介绍闭包之前,首先解释在随后的测试实例中会使用的assert测试函数,这个方法有别于alert()测试,有很大的改进。 assert()测试方法 #...

    vspiders 评论0 收藏0
  • 时器学习:利用时器分解耗时任务案例

    摘要:对于执行时间过长的脚本,有的浏览器会弹出警告,说页面无响应。一个代码块长时间运行,将会导致其他任何任务都必须等待。而其他浏览器,比如上的浏览器,将默认终止运行时间超过秒钟的脚本。利用定时器来分解任务,关键点有两个。实际上每行耗时约左右。 对于执行时间过长的脚本,有的浏览器会弹出警告,说页面无响应。有的浏览器会直接终止脚本。总而言之,浏览器不希望某一个代码块长时间处于运行状态,因为js是...

    Nino 评论0 收藏0
  • 忍者级别的JavaScript函数操作

    摘要:我们需要知道的是,对于而言,匿名函数是一个很重要且具有逻辑性的特性。通常,匿名函数的使用情况是创建一个供以后使用的函数。截图自忍者秘籍通过完善之前对匿名函数的粗略定义,我们可以修复解决这个问题。 从名字即可看书,此篇博客总结与《JavaScript忍者秘籍》。对于JavaScript来说,函数为第一类型对象。所以这里,我们主要是介绍JavaScript中函数的运用。 系列博客地址:h...

    suemi 评论0 收藏0
  • 《javascript忍者秘籍》补遗-01

    摘要:第二例这段代码是用来做将断言测试分组的,代码多了些,问题自然也更多了些。首先作者使用了自执行方法封闭了作用域,使用来指向全局对象,进而产生全局可访问的属性。没想到,久负盛名,豆瓣评分的大作,作者的光环,代码风格居然是如此的不谨慎。 第二章中 作者给了几个简单的断言例子,思路与方向是极不错的,创造JQ的大神,思想高度绝对无法让我质疑的,但是代码的功底细节,实在是让人不敢恭维。 第一例: ...

    Eric 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<