热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

前端知识体系(一)浏览器机制以及进程线程的关系

看了一篇大神的博客,对前端学习体系突然明悟了起来。于是准备参考着大神的脚步开始体系化的学习。博客链接:https:segmentfault.coma1190000013662126

看了一篇大神的博客,对前端学习体系突然明悟了起来。于是准备参考着大神的脚步开始体系化的学习。博客链接:https://segmentfault.com/a/1190000013662126。

很多时候被问到从输入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模式等等内容

第一部分:浏览器进程以及进程线程关系

一、浏览器进程

首先我们了解一下官方的进程和线程的概念。



  • 进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)

  • 线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程)

然后我们的浏览器是多进程的,每打开一个Tab页,就相当于创建了一个独立的浏览器进程。浏览器包括以下几个主要的进程:



  1. Browser进程:浏览器的主控进程,只有一个。作用有



    • 负责浏览器界面显示,与用户交互。如前进,后退等

    • 负责各个页面的管理,创建和销毁其他进程

    • 将Renderer进程得到的内存中的Bitmap,绘制到用户界面上

    • 网络资源的管理,下载等



  2. 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建

  3. GPU进程:最多一个,用于3D绘制等

  4. 浏览器渲染进程(浏览器内核)(Renderer进程,内部是多线程的):默认每个Tab页面一个进程,互不影响。主要作用为



    • 页面渲染,脚本执行,事件处理等



而在这么多进程之中浏览器渲染进程是最重要的,因为它包括很多线程,而这些线程起了页面渲染执行显示的主要作用。以下列举一些主要的线程:



  1. GUI渲染线程



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

    • 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行

    • 注意,GUI渲染线程与JS引擎线程是互斥的,当JS引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。



  2. JS引擎线程



    • 也称为JS内核,负责处理Javascript脚本程序。

    • JS引擎线程负责解析Javascript脚本,运行代码。

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

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



  3. 事件触发线程



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

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

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

    • 注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)





  4. 定时触发器线程



    • 传说中的setIntervalsetTimeout所在线程

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

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

    • 注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。



  5. 异步http请求线程



    • 在XMLHttpRequest在连接后是通过浏览器新开一个线程请求

    • 将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由Javascript引擎执行。



那么这么多线程,他们之间有什么关系呢?

首先GUI渲染线程与JS引擎线程互斥

由于Javascript是可操纵DOM的,如果在修改这些元素属性同时渲染界面(即JS线程和UI线程同时运行),那么渲染线程前后获得的元素数据就可能不一致了。

因此为了防止渲染出现不可预期的结果,浏览器设置GUI渲染线程与JS引擎为互斥的关系,当JS引擎执行时GUI线程会被挂起,
GUI更新则会被保存在一个队列中等到JS引擎线程空闲时立即被执行。


JS阻塞页面加载

从上述的互斥关系,可以推导出,JS如果执行时间过长就会阻塞页面。

譬如,假设JS引擎正在进行巨量的计算,此时就算GUI有更新,也会被保存到队列中,等待JS引擎空闲后执行。
然后,由于巨量计算,所以JS引擎很可能很久很久后才能空闲,自然会感觉到巨卡无比。

所以,要尽量避免JS执行时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞的感觉。

 

那么接下来是主控进程Browser进程对渲染进程的控制过程。



  • Browser进程收到用户请求,首先需要获取页面内容(譬如通过网络下载资源),随后将该任务通过RendererHost接口传递给Render进程

  • Renderer进程的Renderer接口收到消息,简单解释后,交给渲染线程,然后开始渲染



    • 渲染线程接收请求,加载网页并渲染网页,这其中可能需要Browser进程获取资源和需要GPU进程来帮助渲染

    • 当然可能会有JS线程操作DOM(这样可能会造成回流并重绘)

    • 最后Render进程将结果传递给Browser进程



  • Browser进程接收到结果并将结果绘制出来。

js运行机制

最后由于js是单线程,所以对于任务的执行自然会有一个顺序,称之为任务队列,所有任务需要排队,前一个任务结束,才会执行后一个任务。如果前一个任务耗时很长,后一个任务就不得不一直等着。所以任务又分为两种,一种是同步任务:指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务。另一种是异步任务:指的是不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。

总体来说,他的运行机制是这样的:

(1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
(2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
(3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
(4)主线程不断重复上面的第三步。

只要主线程空了,就会去读取"任务队列",这就是Javascript的运行机制。这个过程会不断重复。

主线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,它们在"任务队列"中加入各种事件(click,load,done)。只要栈中的代码执行完毕,主线程就会去读取"任务队列",依次执行那些事件所对应的回调函数。

接下来看一个实例:

所以这段程序的执行结果是:

然而我们这样笼统的分为同步任务和异步任务并不能非常精确到每一种事件,所以在此基础上我们又分了宏任务和微任务。



  • macro-task(宏任务):包括整体代码script,setTimeout,setInterval

  • micro-task(微任务):Promise,process.nextTick

那么现在的执行机制变成了:



  • 执行一个宏任务,过程中如果遇到微任务,就将其放到微任务的【事件队列】里

  • 当前宏任务执行完成后,会查看微任务的【事件队列】,并将里面全部的微任务依次执行完

将同步任务异步任务和宏任务微任务相结合,便是更为准确的js运行机制。接下来请看网络盗图:



  • 整体的script(作为第一个宏任务)开始执行的时候,会把所有代码分为两部分:“同步任务”、“异步任务”;

  • 同步任务会直接进入主线程依次执行;

  • 异步任务会再分为宏任务和微任务;

  • 宏任务进入到Event Table中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到Event Queue中;

  • 微任务也会进入到另一个Event Table中,并在里面注册回调函数,每当指定的事件完成时,Event Table会将这个函数移到Event Queue中;

  • 当主线程内的任务执行完毕,主线程为空时,会检查微任务的Event Queue,如果有任务,就全部执行,如果没有就执行下一个宏任务;

  • 上述过程会不断重复,这就是Event Loop事件循环;

接下来请看实例:

第一次循环执行了两个同步任务,打印了2、4,接下来检查微任务队列,发现.then函数,于是执行函数打印出3。接下来执行异步任务setTimeout,于是打印出来1。

所以最后的结果是2、4、3、1。

接下来难度升级,请看实例2:

他的执行过程是:


1.add()是同步任务,直接执行,打印1;

2.add()里面的setTimeout是异步任务且宏函数,记做timer1放到宏函数队列;

3.add()下面的setTimeout是异步任务且宏函数,记做timer2放到宏函数队列;

4.new Promise是同步任务,直接执行,打印4;

5.Promise里面的setTimeout是异步任务且宏函数,记做timer3放到宏函数队列;

6.Promise里面的for循环,同步任务,执行代码;

7.Promise.then是微任务,放到微任务队列;

8.console.log(8)是同步任务,直接执行,打印8;

9.此时主线程任务执行完毕,检查微任务队列中,有Promise.then,执行微任务,发现有setTimeout是异步任务且宏函数,记做timer4放到宏函数队列;

10.微任务队列中的console.log(7)是同步任务,直接执行,打印7;

11.微任务执行完毕,第一次循环结束;

12.检查宏任务Event Table,里面有timer1、timer2、timer3、timer4,四个定时器宏任务,按照定时器延迟时间得到可以执行的顺序,即Event Queue:timer2、timer4、timer3、timer1,取出排在第一个的timer2;

13.取出timer2执行,console.log(3)同步任务,直接执行,打印3;

14.没有微任务,第二次Event Loop结束;

15.取出timer4执行,console.log(6)同步任务,直接执行,打印6;

16.没有微任务,第三次Event Loop结束;

17.取出timer3执行,console.log(5)同步任务,直接执行,打印5;

18.没有微任务,第四次Event Loop结束;

19.取出timer1执行,console.log(2)同步任务,直接执行,打印2;

20.没有微任务,也没有宏任务,第五次Event Loop结束;

21.结果:1,4,8,7,3,6,5,2。
本文参考:
https://www.jianshu.com/p/e06e86ef2595
https://segmentfault.com/a/1190000013662126
http://www.ruanyifeng.com/blog/2014/10/event-loop.html
https://segmentfault.com/a/1190000012925872
 

推荐阅读
  • Android实战——jsoup实现网络爬虫,糗事百科项目的起步
    本文介绍了Android实战中使用jsoup实现网络爬虫的方法,以糗事百科项目为例。对于初学者来说,数据源的缺乏是做项目的最大烦恼之一。本文讲述了如何使用网络爬虫获取数据,并以糗事百科作为练手项目。同时,提到了使用jsoup需要结合前端基础知识,以及如果学过JS的话可以更轻松地使用该框架。 ... [详细]
  • 单页面应用 VS 多页面应用的区别和适用场景
    本文主要介绍了单页面应用(SPA)和多页面应用(MPA)的区别和适用场景。单页面应用只有一个主页面,所有内容都包含在主页面中,页面切换快但需要做相关的调优;多页面应用有多个独立的页面,每个页面都要加载相关资源,页面切换慢但适用于对SEO要求较高的应用。文章还提到了两者在资源加载、过渡动画、路由模式和数据传递方面的差异。 ... [详细]
  • 第8章 使用外部和内部链接
    8.1使用web地址LearnAboutafricanelephants. ... [详细]
  • 前言:关于跨域CORS1.没有跨域时,ajax默认是带cookie的2.跨域时,两种解决方案:1)服务器端在filter中配置详情:http:blog.csdn.netwzl002 ... [详细]
  • 跨站的艺术XSS Fuzzing 的技巧
    作者|张祖优(Fooying)腾讯云云鼎实验室对于XSS的漏洞挖掘过程,其实就是一个使用Payload不断测试和调整再测试的过程,这个过程我们把它叫做F ... [详细]
  • 初级_vue.js初级教程01.简介
    篇首语:本文由编程笔记#小编为大家整理,主要介绍了vue.js初级教程--01.简介相关的知识,希望对你有一定的参考价值。Vue特点 ... [详细]
  • Voicewo在线语音识别转换jQuery插件的特点和示例
    本文介绍了一款名为Voicewo的在线语音识别转换jQuery插件,该插件具有快速、架构、风格、扩展和兼容等特点,适合在互联网应用中使用。同时还提供了一个快速示例供开发人员参考。 ... [详细]
  • 本文介绍了通过ABAP开发往外网发邮件的需求,并提供了配置和代码整理的资料。其中包括了配置SAP邮件服务器的步骤和ABAP写发送邮件代码的过程。通过RZ10配置参数和icm/server_port_1的设定,可以实现向Sap User和外部邮件发送邮件的功能。希望对需要的开发人员有帮助。摘要长度:184字。 ... [详细]
  • 【爬虫】关于企业信用信息公示系统加速乐最新反爬虫机制
    ( ̄▽ ̄)~又得半夜修仙了,作为一个爬虫小白,花了3天时间写好的程序,才跑了一个月目标网站就更新了,是有点悲催,还是要只有一天的时间重构。升级后网站的层次结构并没有太多变化,表面上 ... [详细]
  • 三、查看Linux版本查看系统版本信息的命令:lsb_release-a[root@localhost~]#lsb_release-aLSBVersion::co ... [详细]
  • SharePoint 指定配置数据库访问账户“域账户\用户”
    大家在安装sharepoint时都会遇到这个问题,域账户,什么是域账户哪?域账户简单理解就是网路账户,与本地账户不同,什么是域哪?域就是控制器。一台Windows计算机,它要么隶属 ... [详细]
  • 一.常见基于身份识别进行反爬1通过headers字段来反爬headers中有很多字段,这些字段都有可能会被对方服务器拿过来进行判断是否为爬虫1.1通过headers中的User-A ... [详细]
  • 【基础部分】之SMTP相关配置
    SMTP一、准备工作修改两个主机的主机名1.mailqq.qq.com2.mail163.163.com先配置dns邮件域名在mailqq.qq.com主机上配置dns配置etcn ... [详细]
  • 最近在做一个项目,但是由于之前一个项目也同时部署在同一台tomcat下,所以出现了sessionId的冲突,描述一下冲突过程:1.打开浏览器访问第一个项目(配置在根目录)2.打开另一标签 ... [详细]
  • 《从零构建前后星散的web项目》:前端相识过关了吗?
    #前端基本架构和硬核引见手艺栈的挑选起首我们构建前端架构须要对前端生态圈有统统相识,而且最好带有肯定的手艺前瞻性,好的手艺架构能够日后会轻易的扩大,削减重构的次数,纵然重构也不须要 ... [详细]
author-avatar
蛋农老张
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有