Javascript引擎单线程机制及setTimeout执行原理说明

setTimeout用法在实际项目中仍是会时常遇到。好比浏览器会聪明的等到一个函数堆栈结束后才改变DOM,若是再这个函数堆栈中把页面背景先从白色设为红色,再设回白色,那么浏览器会认为DOM没有发生任何改变而忽略这两句话,所以咱们能够经过setTimeout把“设回白色”函数加入下一个堆栈,那么就能够确保背景颜色发生过改变了(虽然速度很快可能没法被察觉)。javascript

 


总之,setTimeout增长了Javascript函数调用的灵活性,为函数执行顺序的调度提供极大便利。而后,咱们从基础的层面来看看:理解JavaScript的定时器是如何工做的是很是重要的。计时器的执行经常和咱们的直观想象不一样,那是由于JavaScript引擎是单线程的。咱们先来认识一下下面三个函数是如何控制计时器的。java

var id = setTimeout(fn, delay); - 初始化一个计时器,而后在指定的时间间隔后执行。该函数返回一个惟一的标志ID(Number类型),咱们可使用它来取消计时器。

var id = setInterval(fn, delay); - 和setTimeout有些相似,但它是连续调用一个函数(时间间隔是delay参数)直到它被取消。

clearInterval(id);, clearTimeout(id); - 使用计时器ID(setTimeout 和 setInterval的返回值)来取消计时器回调的发生

 

有一个基本的概念你得记住了:程序员

时间延迟不能被保证。什么意思,就是说你这样写setTimeout(fn, 500)并不表明fn确定在500毫秒以后立刻就执行,延迟极可能会更长。由于 JavaScript 是单线程语言,全部的异步事件(包括计时器、鼠标事件或者一个 XMLHttpRequest 完成)仅仅当程序执行期间有缺口的时候才会执行,不是你规定了何时就何时执行,要知道程序员不是万能的,你写的东西最终仍是要看浏览器脸色的。浏览器

 

用一个很好的图表加以说明:异步

 

JavaScript引擎用单线程运行也是有意义的,单线程没必要理会线程同步这些复杂的问题,问题获得简化.函数

那么单线程的JavaScript引擎是怎么配合浏览器内核处理这些定时器和响应浏览器事件的呢?
下面结合浏览器内核处理方式简单说明.spa

浏览器内核实现容许多个线程异步执行,这些线程在内核制控下相互配合以保持同步.假如某一浏览器内核的实现至少有三个常驻线 程:javascript引擎线程,界面渲染线程,浏览器事件触发线程,除些之外,也有一些执行完就终止的线程,如Http请求线程,这些异步线程都会产 生不一样的异步事件,下面经过一个图来阐明单线程的JavaScript引擎与另外那些线程是怎样互动通讯的.虽然每一个浏览器内核实现细节不一样,但这其中的 调用原理都是大同小异.线程

由图可看出,浏览器中的JavaScript引擎是基于事件驱动的,这里的事件可看做是浏览器派给它的各类任务,这些任务能够源自 JavaScript引擎当前执行的代码块,如调用setTimeout添加一个任务,也可来自浏览器内核的其它线程,如界面元素鼠标点击事件,定时触发 器时间到达通知,异步请求状态变动通知等.从代码角度看来任务实体就是各类回调函数,JavaScript引擎一直等待着任务队列中任务的到来.因为单线 程关系,这些任务得进行排队,一个接着一个被引擎处理.code

 

IE8及其以前的IE版本更新间隔为15.6毫秒。假设你设定的setTimeout延迟为16.7ms,那么它要更新两个15.6毫秒才会该触发延时。这也意味着无端延迟了 15.6 x 2 - 16.7 = 14.5毫秒。blog

           16.7ms
DELAY: |------------|

CLOCK: |----------|----------|
         15.6ms    15.6ms

因此即便你给setTimeout设定的延时为0ms,它也不会当即触发。目前Chrome与IE9+浏览器的更新频率都为4ms(若是你使用的是笔记本电脑,而且在使用电池而非电源的模式下,为了节省资源,浏览器会将更新频率切换至于系统时间相同,也就意味着更新频率更低)。

退一步说,假使timer resolution可以达到16.7ms,它还要面临一个异步队列的问题。由于异步的关系setTimeout中的回调函数并不是当即执行,而是须要加入等待队列中。但问题是,若是在等待延迟触发的过程当中,有新的同步脚本须要执行,那么同步脚本不会排在timer的回调以后,而是当即执行

 

让咱们用一个例子来阐明setTimeout和setInterval之间的区别:

setTimeout(function(){
    /* Some long block of code... */
    setTimeout(arguments.callee, 10);
  }, 10);
  
  setInterval(function(){
    /* Some long block of code... */
  }, 10);

这两句代码乍一看没什么差异,可是它们是不一样的。setTimeout回调函数的执行和上一次执行之间的间隔至少有10ms(可能会更多,但不会少于10ms),而setInterval的回调函数将尝试每隔10ms执行一次,不论上次是否执行完毕。

在这里咱们学到了不少知识,总结一下:

JavaScript引擎是单线程的,强制全部的异步事件排队等待执行

setTimeout 和 setInterval 在执行异步代码的时候有着根本的不一样

若是一个计时器被阻塞而不能当即执行,它将延迟执行直到下一次可能执行的时间点才被执行(比指望的时间间隔要长些)

若是setInterval回调函数的执行时间将足够长(比指定的时间间隔长),它们将连续执行而且彼此之间没有时间间隔。

相关文章
相关标签/搜索