前段时间刚看完《JS忍者秘籍》,虽然说是15年出版的,有些东西是过期了,但像对原型链、闭包、正则、定时器之类的机制倒是不会过期的,里面不少东西都讲的很细,仍是值得一读的,本文将对这本书中对定时器机制的部分进行详细的解析,若是喜欢的话能够点波赞/关注,支持一下,但愿你们看完本文能够有所收获。ajax
我的博客了解一下: obkoro1.com
阅读本文以前,推荐先阅读Js 的事件循环(Event Loop)机制以及实例讲解这篇文章来理解背后发生的事情,本文对事件循环机制不会很仔细的讲解。promise
因为JS的单线程特性,定时器提供了一种跳出单线程限制的方法,即让一段代码在必定毫秒以后,再异步执行。浏览器
直接引用忍者秘籍中的图片:闭包
setTimeout
和setInterval
的时候最好将其赋值给一个变量,以便取消定时器。Vue
的时候,setTimeout
和setInterval
的this指向的是window对象,访问不到组件数据以及方法。Vue
的时候,路由跳转并不会销毁setInterval
,可是组件已经销毁了,这会致使问题。setTimeout
/setInterval
没法保证准时执行回调函数的。setInterval
调用有可能会被废弃以及setInterval
的连续执行第三点和第四点的解决方法能够参考我以前写的Vue 实践过程当中的几个问题。异步
接下来要讲的是第五点和第六点,这两点是最重要,也是本文要重点解析的内容。函数
下面来看忍者秘籍中的栗子:oop
让咱们看看这里发生了什么事情:post
setTimeout
以及setInterval
,setTimeout
先设定。同时发生了这么多事情,因为js的单线程特性,当线程正在执行状态,有异步事件触发时,它就会排队,而且在线程空闲时才进行执行。学习
这里的异步事件包括:鼠标单击,定时器触发,ajax请求、promise等事件。
让咱们回到栗子中:this
栗子中首先有一个18毫秒的代码块要执行,在这18毫秒中只能执行这段代码块,其余事件触发了以后只能排队等待执行。
在代码块还在运行期间,第6毫秒的时候,发生了一个鼠标单击事件,以及第10毫秒时的setTimeout
和setInterval
两个处理程序,这三个事件不能当即执行,而是被添加到等待执行的队列中。
18毫秒的时候代码块结束执行,有三个任务在排队等待执行,根据先进先出的原则,此时会先执行click事件,setTimeout
和setInterval
将继续排队等待执行。
在click事件执行时,第20毫秒处,第二个setInterval
也到期了,由于此时已经click事件占用了线程,因此setInterval
仍是不能被执行,而且由于此时队列中已经有一个setInterval
正在排队等待执行,因此这一次的setInterval
的调用将被废弃。
浏览器不会对同一个setInterval处理程序屡次添加到待执行队列。
setTimeout
/setInterval
没法保证准时执行回调函数click事件在第28毫秒处结束执行,有两个任务(setTimeout
和setInterval
)正在等待执行,遵循先进先出的原则,setTimeout
早于setInterval
设定,因此先执行setTimeout
。
so:咱们指望在第10毫秒处执行的setTimeout
处理程序,最终会在第28毫秒处才开始执行,这就是上文提到的setTimeout
/setInterval
没法保证准时执行回调函数。
在30毫秒处,setInterval
又触发了,由于队列中已经有setInterval
在排队,因此此次的触发又做废了。
setTimeout
执行结束,在第36毫秒处,队列中的setInterval
处理程序才开始执行,setInterval
须要执行6毫秒。
在第40毫秒的时候setInterval
再次触发,由于此时上一个setInterval
正在执行期间,队列中并无setInterval
在排队,此次触发的setInterval
将进入队列等候。
因此:setInterval
的处理时长不能比设定的间隔长,不然setInterval
将会没有间隔的重复执行
第42毫秒的时候,第一个setInterval
结束,而后队列中的setInterval
当即开始执行,在48毫秒的时候完成执行。而后50毫秒的时候再次触发setInterval
,此时没有任务在排队,将会当即执行。
上文说了,setInterval
的处理时长不能比设定的间隔长,不然setInterval
将会没有间隔的重复执行。
可是对这个问题,不少状况下,咱们并不能清晰的把控处理程序所消耗的时长,为了咱们能按照必定的间隔周期性的触发定时器,忍者秘籍中提供了下面这种使用方法:
// 实际上我不止在忍者秘籍中见过,在不少地方都见过这种技术。 setTimeout(function repeatMe(){ // do something setTimeout(repeatMe,10); // 执行完处理程序的内容后,在末尾再间隔10毫秒来调用该程序,这样就能保证必定是10毫秒的周期调用 },10)
事实上,关于任务队列并非只有简单的排队而已,忍者秘籍中提到为了方便,使用了这个概念,若是但愿能更清晰的了解背后的机制,再次推荐阅读一下:Js 的事件循环(Event Loop)机制以及实例讲解,
这上面全部一切,都是由js单线程特性致使的,因此会有事件排队、先进先出、setInterval调用被废弃、定时器没法保证准时执行回调函数以及出现setInterval的连续执行,时刻记住这一特性,不少关于事件执行顺序的问题都能想的通,而且找出解决方法。
我的blog and 掘金我的主页,如需转载,请放上原文连接并署名。码字不易,感谢支持!本人写文章本着交流记录的心态,写的很差之处,不撕逼,可是欢迎指点。
若是喜欢本文的话,欢迎关注个人订阅号,漫漫技术路,期待将来共同窗习成长。
以上2018.6.17
JS忍者秘籍第8章:驯服线程和定时器