概述
JS引擎运行机制
同步?异步?
线程与进程
- 进程是一个具有一定独立功能的程序在一个数据集上的一次动态执行的过程,是操作系统进行资源分配和调度的一个独立单位,是应用程序运行的载体。
- 线程是程序执行中一个单一的顺序控制流程,是程序执行流的最小单元。
区别:
- 进程是操作系统分配资源的最小单位,线程是程序执行的最小单位
- 一个进程由一个或多个线程组成,线程是一个进程中代码的不同执行路线;
- 进程之间相互独立,但同一进程下的各个线程之间共享程序的内存空间(包括代码段、数据集、堆等)及一些进程级的资源(如打开文件和信号)。
- 调度和切换:线程上下文切换比进程上下文切换要快得多。
最常看到的一个关于线程与进程的比喻是,进程就像是工厂的车间,线程像是车间里一个一个的工人。
多进程与多线程:
- 多进程指的是在同一个时间里,同一个计算机系统中如果允许两个或两个以上的进程处于运行状态。
- 多线程是指程序中包含多个执行流,即在一个程序中可以同时运行多个不同的线程来执行不同的任务,也就是说允许单个程序创建多个并行执行的线程来完成各自的任务。
单线程与多线程
浏览器包含的进程
浏览器是多进程的,主要包含以下进程
- Browser进程:浏览器的主进程(负责协调、主控),只有一个
- 负责浏览器界面显示,与用户交互。如前进,后退等
- 负责各个页面的管理,创建和销毁其他进程
- 将Renderer进程得到的内存中的Bitmap,绘制到用户界面上
- 网络资源的管理,下载等
-
NPAPI插件进程和Pepper插件进程: 每种类型的插件对应一个进程,仅当使用该插件时才创建,只会被创建一次,可被共享
-
GPU进程: 最多只有一个,并且仅当GPU硬件加速打开的时候才会被创建,主要用于对3D图形加速调用的实现;
-
浏览器渲染进程(浏览器内核)(Renderer进程是多线程的):默认每个Tab页面一个进程,互不影响。
主要作用为页面渲染,脚本执行,事件处理等
blink/webkit的渲染工作主要是在这个进程完成,可能有多个,具体个数允许用户配置; -
其它类型的进程: 包括linux下的Zygote进程,render进程就是有它创建;Sandbox进程,用于安全进制中;
有以下特征:
- Browser进程和页面的渲染是分开的,保证了页面的渲染导致的崩溃不会导致浏览器主界面的崩溃;
- 每个网页是独立的进程,保证了页面之间相互不影响;
- 插件进程也是独立的,插件本身的问题不会影响游览器主界面和网页;
- GPU硬件加速进程也是独立的。
在浏览器中打开一个网页相当于新起了一个进程(进程内有自己的多线程),浏览器有时会将多个进程合并
浏览器多进程的优势
相比于单进程浏览器,多进程有如下优点:
- 避免单个page crash影响整个浏览器
- 避免第三方插件crash影响整个浏览器
- 多进程充分利用多核优势
- 方便使用沙盒模型隔离插件等进程,提高浏览器稳定性
简单点理解:如果浏览器是单进程,那么某个Tab页崩溃了,就影响了整个浏览器,体验有多差;同理如果是单进程,插件崩溃了也会影响整个浏览器;而且多进程还有其它的诸多优势。。。
当然,内存等资源消耗也会更大,有点空间换时间的意思。
Browser进程和浏览器内核(Renderer进程)的通信过程
- Browser进程收到用户请求,首先需要获取页面内容(譬如通过网络下载资源),随后将该任务通过RendererHost接口传递给Render进程
- Renderer进程的Renderer接口收到消息,简单解释后,交给渲染线程,然后开始渲染
- 渲染线程接收请求,加载网页并渲染网页,这其中可能需要Browser进程获取资源和需要GPU进程来帮助渲染
- 当然可能会有JS线程操作DOM(这样可能会造成回流并重绘)
- 最后Render进程将结果传递给Browser进程
- Browser进程接收到结果并将结果绘制出来
WebWorker与SharedWorker
- WebWorker只属于某个页面,不会和其他页面的Render进程(浏览器内核进程)共享
所以Chrome在Render进程中(每一个Tab页就是一个render进程)创建一个新的线程来运行Worker中的JavaScript程序。 - SharedWorker是浏览器所有页面共享的,不能采用与Worker同样的方式实现,因为它不隶属于某个Render进程,可以为多个Render进程共享使用
所以Chrome浏览器为SharedWorker单独创建一个进程来运行JavaScript程序,在浏览器中每个相同的JavaScript只存在一SharedWorker进程,不管它被创建多少次。
看到这里,应该就很容易明白了,本质上就是进程和线程的区别。
SharedWorker由独立的进程管理,WebWorker只是属于render进程下的一个线程
浏览器内核
浏览器内核是多线程,在内核控制下各线程相互配合以保持同步,一个浏览器通常由以下常驻线程组成:GUI 渲染线程、JavaScript引擎线程、定时触发器线程、事件触发线程、异步http请求线程
- GUI渲染线程
负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等;
当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程执行;
Chrome/Safari/Opera用的是Webkit引擎,IE用的是Trident引擎,FireFox用的是Gecko引擎。 - Javascript引擎线程(单线程)
也称为JS内核,负责处理Javascript脚本程序(例如V8引擎);
负责解析Javascript脚本,运行代码;
如果JS执行的时间过长,这样就会造成页面的渲染不连贯,导致页面渲染加载阻塞。
(当浏览器在执行JavaScript程序的时候,GUI渲染线程会被保存在一个队列中,直到JS程序执行完成,才会接着执行。) - 事件触发线程
当事件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理;
这些事件可以是当前执行的代码块如定时任务、也可来自浏览器内核的其他线程如鼠标点击、AJAX异步请求等(JS是单线程,这些事件得等待JS引擎处理)。 - 定时触发器线程
W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms;
浏览器定时计数器并不是由JavaScript引擎计数的,(JavaScript引擎是单线程, 如果处于阻塞线程状态就会影响记计时的准确) - 异步http请求线程
在XMLHttpRequest连接后通过浏览器新开的一个线程请求;
将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件放到 事件队列中等待执行
GUI渲染线程与JavaScript引擎为互斥的关系,当JS引擎执行时GUI线程会被“冻结”,GUI更新会被保存在一个队列中等到JS引擎空闲时立即被执行;
事件循环
JS引擎线程运行的时候,产生堆(heap)和栈(stack),栈中的代码调用各种外部API,我理解的是使用其他线程来执行任务,执行完毕后,它们在"任务队列"中加入各种事件(click,load,done)。
如setTimeout(function(){//函数代码},1000),将调用定时触发器线程来计时,1秒后,回调函数将被推到任务队列。因此定时器的回调函数并不是计时达到设置的时间就立即执行,实际的执行时间不一定准确。
要栈中的代码执行完毕,主线程(JS引擎线程)就会去读取"任务队列",依次执行那些事件所对应的回调函数,这个反复的过程就是事件循环。
任务划分(一)
把任务分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
任务队列类型: microtask queue(微任务队列),macrotask queue(宏任务队列)。
microtask queue: 唯一,整个事件循环当中,仅存在一个;执行为同步,同一个事件循环中的microtask会按队列顺序,串行执行完毕;
macrotask queue: 不唯一,存在一定的优先级(用户I/O部分优先级更高);异步执行,同一事件循环中,只执行一个。
JS执行机制:
判断JS是同步还是异步,同步任务进入主进程执行,异步任务调用外部api执行,当满足触发条件后,被推入任务队列,直到主线程空闲时,才会去队列中查看是否有可执行的异步任务,如果有就推入主进程中执行。
任务队列存在多个,同一任务队列内,按队列顺序被主线程取走;不同任务队列之间,存在着优先级,优先级高的优先获取(如用户I/O)
所有任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。(任务分类方式)
同步任务指的是,在主线程上排队执行的任务,只有前一个任务执行完毕,才能执行后一个任务;
异步任务指的是,不进入主线程、而进入"任务队列"(task queue)的任务,只有"任务队列"通知主线程,某个异步任务可以执行了,该任务才会进入主线程执行。
栗子:
执行结果:
任务划分(更准确):
macro-task(宏任务):包括整体代码script,setTimeout,setInterval ,setImmediate,I/O,各种事件的回调函数(,UI rendering)
micro-task(微任务):Promise,process.nextTick(在node环境下,process.nextTick的优先级高于Promise)
JS执行机制:
执行整体代码这个宏任务,执行的过程发现宏任务或微任务,将其放入对应的任务队列。当整体代码这个宏任务执行完之后,查看是否有可执行的微任务,有则执行,没有则执行下一个宏任务。
栗子2:
执行结果:
参考链接:
- 浏览器进程?线程?傻傻分不清楚!
- JavaScript 运行机制详解:再谈Event Loop
- JS 事件循环机制 - 任务队列、web API、JS主线程的相互协同
- JavaScript异步机制详解
- 从浏览器多进程到JS单线程,JS运行机制最全面的一次梳理
- webkit架构和模块
- JS:事件循环机制、调用栈以及任务队列
- 浏览器UI线程 和 js线程
- JS引擎的执行机制
- https://baijiahao.baidu.com/s?id=1584876586345446053&wfr=spider&for=pc
最后
以上就是轻松夕阳为你收集整理的JS引擎运行机制(事件循环)JS引擎运行机制的全部内容,希望文章能够帮你解决JS引擎运行机制(事件循环)JS引擎运行机制所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复