标签:web安全 回收 操作 int logs 不可 布局 oop bitmap
原文链接:https://www.cnblogs.com/tutuj/p/11025042.html
很多时候被问到从输入url地址之后,会发生什么?很多时候回答都很笼统,没有自己的核心,所以学习一下大神的思路,以下总结的只是骨干,只有将每一个部分都学习到,这样才是一个知识体系,才能很好的理解上下结构与关系。
1. 从浏览器接收url到开启网络请求线程(这一部分可以展开浏览器的机制以及进程与线程之间的关系) 2. 开启网络线程到发出一个完整的http请求(这一部分涉及到dns查询,tcp/ip请求,五层因特网协议栈等知识) 3. 从服务器接收到请求到对应后台接收到请求(这一部分可能涉及到负载均衡,安全拦截以及后台内部的处理等等) 4. 后台和前台的http交互(这一部分包括http头部、响应码、报文结构、cookie等知识,可以提下静态资源的cookie优化,以及编码解码,如gzip压缩等) 5. 单独拎出来的缓存问题,http的缓存(这部分包括http缓存头部,etag,catch-control等) 6. 浏览器接收到http数据包后的解析流程(解析html-词法分析然后解析成dom树、解析css生成css规则树、合并成render树,然后layout、painting渲染、复合图层的合成、GPU绘制、外链资源的处理、loaded和domcontentloaded等) 7. CSS的可视化格式模型(元素的渲染规则,如包含块,控制框,BFC,IFC等概念) 8. JS引擎解析过程(JS的解释阶段,预处理阶段,执行阶段生成执行上下文,VO,作用域链、回收机制等等) 9. 其它(可以拓展不同的知识模块,如跨域,web安全,hybrid模式等等内容
第一部分:浏览器进程以及进程线程关系
一、浏览器进程
首先我们了解一下官方的进程和线程的概念。
然后我们的浏览器是多进程的,每打开一个Tab页,就相当于创建了一个独立的浏览器进程。浏览器包括以下几个主要的进程:
Browser进程:浏览器的主控进程,只有一个。作用有
浏览器渲染进程(浏览器内核)(Renderer进程,内部是多线程的):默认每个Tab页面一个进程,互不影响。主要作用为
而在这么多进程之中浏览器渲染进程是最重要的,因为它包括很多线程,而这些线程起了页面渲染执行显示的主要作用。以下列举一些主要的线程:
GUI渲染线程
JS引擎线程
事件触发线程
注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)
定时触发器线程
setInterval
与setTimeout
所在线程异步http请求线程
那么这么多线程,他们之间有什么关系呢?
首先GUI渲染线程与JS引擎线程互斥
由于JavaScript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。
因此为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JS引擎为互斥的关系,当JS引擎执行时GUI线程会被挂起,
GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行。
从上述的互斥关系,可以推导出,JS如果执行时间过长就会阻塞页面。
譬如,假设JS引擎正在进行巨量的计算,此时就算GUI有更新,也会被保存到队列中,等待JS引擎空闲后执行。
然后,由于巨量计算,所以JS引擎很可能很久很久后才能空闲,自然会感觉到巨卡无比。
所以,要尽量避免JS执行时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。
那么接下来是主控进程Browser进程对渲染进程的控制过程。
Renderer进程的Renderer接口收到消息,简单解释后,交给渲染线程,然后开始渲染
js运行机制
最后由于js是单线程,所以对于任务的执行自然会有一个顺序,称之为任务队列,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。所以任务又分为两种,一种是同步任务:指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。另一种是异步任务:指的是不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
总体来说,他的运行机制是这样的:
(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。 (2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。 (3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。 (4)主线程不断重复上面的第三步。
只要主线程空了,就会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。
主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。
接下来看一个实例:
<script> console.log(‘start‘) //同步任务在主线程上执行,进入执行栈。 setTimeout(function () { //异步任务进入 task table,等到0秒之后进入task queue。 console.log(‘setTimeout1‘); }, 0); console.log(‘end‘); //同步任务在主线程上执行,进入执行栈。 </script>
所以这段程序的执行结果是:
然而我们这样笼统的分为同步任务和异步任务并不能非常精确到每一种事件,所以在此基础上我们又分了宏任务和微任务。
那么现在的执行机制变成了:
将同步任务异步任务和宏任务微任务相结合,便是更为准确的js运行机制。接下来请看网络盗图:
接下来请看实例:
<script> setTimeout(function () { //setTimeout是异步,且是宏函数,放到宏函数队列中 console.log(1) }); new Promise(function (resolve) { //new Promise是同步任务,直接执行,打印2,并执行for循环 console.log(2); for (var i = 0; i < 10000; i++) { i == 9999 && resolve(); } }).then(function () { //promise.then是微任务,放到微任务队列中 console.log(3) }); console.log(4); //console.log(4)同步任务,直接执行,打印4 </script>
第一次循环执行了两个同步任务,打印了2、4,接下来检查微任务队列,发现.then函数,于是执行函数打印出3。接下来执行异步任务setTimeout,于是打印出来1。
所以最后的结果是2、4、3、1。
接下来难度升级,请看实例2:
<script> function add(x, y) { console.log(1) setTimeout(function () { // timer1 console.log(2) }, 1000) } add(); setTimeout(function () { // timer2 console.log(3) }) new Promise(function (resolve) { console.log(4) setTimeout(function () { // timer3 console.log(5) }, 100) for (var i = 0; i < 100; i++) { i == 99 && resolve() } }).then(function () { setTimeout(function () { // timer4 console.log(6) }, 0) console.log(7) }) console.log(8) </script>
他的执行过程是:
标签:web安全 回收 操作 int logs 不可 布局 oop bitmap
原文地址:https://www.cnblogs.com/sundance123/p/13890735.html