浏览器引擎初认识
浏览器引擎有一个主线程和多个后台线程。主线程执行我们的js代码,
后台线程提供api接受主线程的调用或者绘制界面。有ui线程、定时器
触发线程、浏览器事件触发线程、http异步请求线程。
一.ui线程
ui线程有四个作用:
1.解析html文件: 生成节点(每个节点有不同类型(文本、属性还是dom)、不同的默认样式、
多个子节点)树;
2.解析所有内联的、引入的和行内的css代码, 生成另一颗css树;;
3.根据节点树和css树计算每个节点在屏幕上的左上角x、y坐标和宽、高;
4.根据第3步计算结果和第1、2步获得的数据在屏幕上绘制出所有图形;
二.定时器触发线程
js里有两种定时器, setTimeout和setInterval, 因为js执行线程只有一个、是同步阻塞式的,
所以js执行线程自己无法进行计数工作的。那么需要使用定时器计数的时候怎么办呢?js执行线程在
执行函数setTimeout和setInterval时它只是在定时器触发线程里注册了定时任务, 然后立即执行
它的执行栈里面没有执行完的代码。因为计数是一件执行非常快的事, 所以可以认为定时器触发线程
只有一个线程。定时器触发线程完全可以每过1ms遍历一次自己的定时器队列, 每遍历到一个定时器
就给这个定时器时间加1, 如果某个定时器时间到了, 就把这个定时器加到js执行线程的任务队列
里面去,不停地循环。至于js执行线程和定时器触发线程、ui线程等其它线程等, 每个线程获得的执
行时间百分比, 这个完全可以平均给这5个线程设置最大执行时间为20%, 超过20%就立马暂停把当前
执行任务切换成其它线程, 如果不到20%这个线程代码就执行完了, 那也可以立即切换到其它线程并
且不让这个没有要执行的代码的线程参与时间片分配,直到这个线程有新的要执行的代码为止。
浏览器事件触发线程
跟定时器事件触发线程类似, 区别是它没有自己的事件队列,
也不需要轮换遍历事件队列计数。它只
需要监听浏览器有没有任何键盘、鼠标事件发生, 有就丢到js执行线程的事件队列里去, 没有就啥
事不干、也不参与时间片竞争。
http异步请求线程
跟定时器触发线程类似, js执行线程执行发送http请求的代码时, 只是把这个任务塞给http请求线
程并且注册回调事件, http请求线程根据http请求结果把相应的回调事件赛道js执行线程的事件队
列里面去。
js执行线程
js执行线程是这样的: 首先检查有没有要执行的代码, 如果有, 那就一条道走到黑,直到把要执行
的代码执行完为止;然后去查询它的事件队列看有没有事件任务代码需要它执行, 有的话也是一条道走
到黑, 不执行完自己事件任务队列里的任务代码, 不会再去检查有没有要执行的js代码。js执行线
程就是这样一轮一轮地循环进行。js通过api给ui线程、定时器线程、http请求线程塞任务, 定时器
线程、浏览器事件线程、http请求线程也可以给js线程塞任务。所以js执行线程串起了两组线程,
一组线程是ui线程,负责绘制页面, 一组线程是定时器线程、浏览器事件线程、http请求线程负责提
供辅助功能。js里有四种异步任务, 他们在队列里的优先级是:
promise > 浏览器事件 > setTimeout > ajax
那么异步任务只在js执行线程本轮执行过程中被放入js执行线程的任务队列还是在本轮执行结束下一
轮执行完成之前呢?
js执行线程和ui线程互斥(因为js执行线程可以导致UI变化, js线程也需要获取ui线程里的数据;如果
不互斥, 那ui线程不知道用什么数据去绘制界面, js线程也可能反复取用过期数据导致自己给自己造
成死循环), 但是与其它线程不互斥。
## 下一个
下一个讨论浏览器多进程问题