JS的运行机制

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

1、GUI渲染线程

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

  。当页面需要重绘或者由于某种操作引发页面回流时,该线程就会执行

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

2、JS引擎线程

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

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

  。JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页面中无论什么时候都只有一个js线程在执行js程序

  。同样,JS引擎线程和GUI渲染线程是互斥的,所以如果JS线程执行的时间过长,这样会造成页面的渲染不连贯,导致页面渲染加载阻塞

3、事件触发线程

  。该线程归属于浏览器而不是JS引擎线程,用来控制事件循环。(可以这样理解,JS引擎自己都忙不过来,需要浏览器另开线程协助)

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

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

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

4、定时触发器线程

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

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

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

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

5、异步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(因为只有可执行栈内空了后才会主动读取事件队列)

 

原文地址:https://www.cnblogs.com/lvruifang/p/9406793.html