JS的运行机制

首先咱们应该先知道浏览器内核渲染进程是由多线程组成的,其中主要包括如下几个javascript

一、GUI渲染线程java

  。主要负责渲染浏览器界面,解析HTML和CSS,构建DOM树和RenderObject树,布局和绘制等浏览器

  。当页面须要重绘或者因为某种操做引起页面回流时,该线程就会执行多线程

  。注意,GUI渲染线程和JS引擎线程是互斥的,当JS引擎线程运行的时候,GUI渲染线程就会被挂起,GUI更新会被保存在一个队列中,等待JS引擎空闲下来当即执行异步

二、JS引擎线程函数

  。又称为JS内核,主要负责处理javascript脚本程序布局

  。JS引擎负责解析javascript脚本,运行代码spa

  。JS引擎一直等待着任务队列中任务的到来,而后加以处理,一个Tab页面中不管何时都只有一个js线程在执行js程序线程

  。一样,JS引擎线程和GUI渲染线程是互斥的,因此若是JS线程执行的时间过长,这样会形成页面的渲染不连贯,致使页面渲染加载阻塞队列

三、事件触发线程

  。该线程归属于浏览器而不是JS引擎线程,用来控制事件循环。(能够这样理解,JS引擎本身都忙不过来,须要浏览器另开线程协助)

  。当JS引擎执行代码块 如setTimeOut(也可来自浏览器内核的其余线程,如鼠标点击,AJAX异步请求等)时,会把对应的任务添加到事件触发线程中

  。当对应的事件符合触发条件被触发时,该线程会把该事件添加到待处理的任务队列的队尾,等待JS引擎处理

  。因为JS引擎是单线程的,因此这些待处理任务队列中的事件都得排队等待JS引擎处理(JS引擎空闲时才会去执行)

四、定时触发器线程

  。传说中的setTimeOut和setInterval所在的线程

  。浏览器定时计数器并非由javascript引擎计数的(由于JS引擎是单线程的,若是线程处于阻塞状态就会影响计时的准确性)

  。所以经过单独的线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)

  。须要注意,W3C在HTML标准中规定,setTimeOut小于4ms的时间间隔算为4ms

五、异步http请求线程

  。在XMLHttpRequest链接后是经过浏览器新开一个线程请求

  。在检测到状态变动时,若是设置有回调函数,异步线程就产生状态变动事件,将这个回调再放入事件队列中,由js引擎空闲时执行

咱们都知道JS引擎是单线程的,这是由于JS的做用主要是与用户互动,以及操做DOM,这决定了他只能是单线程的,不然会带来不少同步的问题,假如JS有两个线程,同一时间一个线程再某个DOM节点上添加东西,一个线程再删除该DOM节点,这时候就会出现问题

而后咱们还须要理解一些概念:

.JS分为同步任务和异步任务

1.同步任务都在主线程上执行,造成一个执行栈

2.主线程以外,事件触发线程管理着一个任务队列,只要异步任务有告终果,就会在任务队列中添加一个事件

3.一旦执行栈中全部同步任务执行完毕,此时,JS引擎空闲,系统就会读取任务队列,将可执行的异步任务添加到可执行栈中,开始执行

如此循环上面的步骤

看到这里咱们大概明白了,为何setTimeOut推入的事件没有在规定的时间执行,这是由于,当它推入到事件队列中时,主线程尚未空闲,JS引擎还在执行主线程的任务,因此天然会有偏差

关于定时器:

上述事件循环机制的核心是:JS引擎线程和事件触发线程

但事件上还有一些隐藏的细节,譬如,调用setTimeOut后,是如何等待特定时间后才添加到事件队列中的?

是JS引擎检测的么?固然不是了。它是由定时器线程控制

为何要单独的定时器线程?由于JS引擎是单线程的,若是处于阻塞线程状态就会影响计时的准确,所以颇有必要单独开一个线程来计时

何时会用到定时器线程?当使用setTimeOut或setInterval时,他须要定时器线程计时,计时完成后就会将特定的事件推入事件队列中。

譬如:

setTimeout(function(){

  console.log('hello!');

},1000);

这段代码的做用是1000毫秒计时完成后(由定时器线程计时),将回调函数推入事件队列中,等待主线程执行

setTimeout(function(){

  console.log('hello!');

},0);

console.log('begin');

这段代码的效果是最快的时间内将回调函数推入事件队列中,等待主线程执行

注意

执行结果是先begin后hello!

虽然代码的本意是0毫秒后就推入事件队列,可是W3C在HTML标准中规定,要求setTimeout中低于4ms的时间间隔算为4ms

就算不等待4ms,就算假设0毫秒就推入事件队列,也会先执行begin(由于只有可执行栈内空了后才会主动读取事件队列)

相关文章
相关标签/搜索