进程与线程
首先需要了解进程与线程的概念,具体可以参考我的另一个文章 《进程与线程》
多进程的浏览器
浏览器是多进程的,有一个主进程,以及每一个tab也都会有一个新的进程(某些情况下多个tab会合并成一个进程)。进程包括浏览器主进程,插件进程,GPU,tab页(浏览器内核)等等...
主要有:
- 浏览器进程(Browser进程):浏览器的主进程,负责协调、主控,只有一个(无论打开几个tab或几个弹窗)
- 负责浏览器界面显示,与用户交互,如前进,后退等
- 负责各个页面的管理,创建和销毁其他进程
- 将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
- 网络资源的管理,下载等
- 第三方插件进程:每种类型的插件对应一个进程,仅当使用该插件时才创建
- GPU进程:最多一个,用于3D绘制
- 浏览器渲染进程(浏览器内核(Renderer进程)):浏览器渲染进程(render进程),即通常说的浏览器内核,主要作用是:页面渲染、脚本执行、事件处理等。每一个标签页的打开都会创建一个Render进程,并且互不影响。默认的话一个标签页对应一个Render进程,但是,有时候浏览器会将多个进程合并,如打开了多个空白标签页
如图所示,Edge浏览器拥有一个主进程,和26个子进程,大多数子进程都是一个一个tab页。每开启一个tab页,浏览器就会多一个子进程,反之则会减少一个子进程
多线程的浏览器内核进程(Renderer进程)
浏览器渲染进程(render进程,也成为浏览器内核),是我们前端开发人员最需要关注的。它主要的作用:页面渲染、脚本执行、事件处理等。它包含一下5种线程:
- GUI渲染线程(有且只有一个)
- 负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
- 当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行。
- 注意,GUI渲染线程与JS引擎线程是互斥的。当JS引擎执行时GUI线程会被挂起(相当于被冻结了),GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行。
- JS引擎线程(有且只有一个)
- JS引擎线程也称为JS内核,负责处理Javascript脚本程序,解析Javascript脚本,运行代码
- JS引擎一直等待着任务队列中任务的到来,然后加以处理,一个Tab页(renderer进程)中无论什么时候都只有一个JS线程在运行JS程序
- 注意:GUI线程和JS引擎线程互斥。所以当JS加载事件过长时,会造成页面渲染不连贯,导致阻塞页面加载。这就是为什么建议将<script>标签写在body的最末端。
- 事件触发线程
- 事件触发线程归属于浏览器,而不是属于JS引擎,JS引擎处理的事务过多,需要浏览器另开线程来进行协助
- JS是采用事件驱动(event-driven)机制,来响应用户操作的,事件触发线程通过维护事件循环和事件队列等的方式,来响应和处理事件
- 当处理一些不能立即执行的函数或者其他的代码时,会将对应的任务在其可以触发的时机,添加到事件队列的末端
- 事件循环机制会在JS引擎线程空闲时,循环访问事件队列的头部,如果有函数,则会将该函数推到执行栈中并立即执行。
- 定时器触发线程(多个)
- 即setInterval与setTimeout所在线程
- 浏览器定时计数器并不是由JS引擎计数的,因为JS引擎是单线程的, 如果处于阻塞线程状态就会影响记计时的准确性
- 因此使用单独线程来计时并触发定时器,计时完毕后,添加到事件队列中,等待JS引擎空闲后执行,所以定时器中的任务在设定的时间点不一定能够准时执行,定时器只是在指定时间点将任务添加到事件队列中
- 注意:W3C在HTML标准中规定,定时器的定时时间不能小于4ms,如果是小于4ms,则默认为4ms
- 网络请求线程
- XMLHttpRequest连接后通过浏览器新开一个线程请求
- 检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将回调函数放入事件队列中,等待JS引擎空闲后执行
重新梳理一下线程之间的关系
-
GUI渲染引擎和JS执行引擎互斥,当GUI渲染引擎解析html时,发现了script标签时,会立即挂起解析html的任务,然后开始解析js代码,直到所有js代码执行完成后,继续执行html解析渲染。
-
JS是单线程的,所以在处理某些任务如请求服务端数据,定时任务等,自顾不暇,需要其他兄弟线程的协助。
-
JS是基于事件驱动的,其需要一种事件循环机制和事件队列的夹持,即需要事件触发线程的配合,和其他线程之间亲密合作。
- 为什么JS是单线程的:
JS执行引擎是单线程的,就是说,每一个浏览器内核(Render进程中),有且只有一个JS执行引擎(web worker另谈),为什么只能有一个?这和JS的用途有关,JS作为浏览器的脚本语言,主要用处是与用户进行交互,以及操作DOM的,如果JS是多线程,当在不同线程进行不同的DOM操作,会造成更复杂的一些问题,所以从一开始,JS就是单线程的。
- 什么是同步任务:
JS单线程就意味着,我的代码必须是从上到下,一行一行执行。一个代码块执行完毕之后,才能进行另一个代码块的执行,这就是同步执行代码。 (定义:同步执行:代码从上到下,一行一行执行,下方代码块的执行必须是等待其前面代码全部执行完成之后才能执行,若代码执行中报错,则其下方代码不再执行)
- 存在什么问题:
那么此时,我们想要从服务端获取某一些数据然后渲染到页面上,如果仍然是同步执行,我们需要等待数据全部加载完成之后,才能执行接下来的代码;如果这个数据的请求特别慢,那么页面就极其不友好。
- 如何解决:(异步回调)
异步回调是两个词,异步指的是,JS执行引擎会将某些任务挂起,交由他的兄弟线程进行处理,等到兄弟线程处理完成之后,JS执行引擎再回去执行其回调函数,他不会阻塞代码的执行,异步代码执行错误,不会影响其他同步代码的执行。而回调函数指的是,我先定了一个函数,但是目前我不想调用,当在某个事情发生或者到了某个特定的时间,我会去调用这个函数,这个就称为回调函数。
异步执行基于回调函数。异步执行基于回调函数。异步执行基于回调函数。
什么任务会触发异步:
- 网络请求,如ajax请求(交由异步http线程处理)
- 定时任务,setTimeout, setInterval(交友定时器线程)
回调函数的应用场景:
- 网络请求成功或者失败的回调函数
- 定时任务在到达指定事件后触发的函数
- 用户界面交互时发出的事件的处理函数
参考文档
从输入URL到页面成功展示到浏览器的过程?_尹成诺的博客-CSDN博客