首页 > 编程知识 正文

事件循环机制eventloop,海岛奇兵一周事件循环

时间:2023-05-03 08:05:01 阅读:188129 作者:1103

打开一个tab页面就有一个渲染进程!!!

进程与线程

进程和线程的区别:

进程是cpu资源分配的最小单位(是能拥有资源和独立运行的最小单位)线程是cpu调度的最小单位(线程是建立在进程的基础上的一次程序运行单位,一个进程中可以有多个线程) 进程是一个工厂,工厂有它的独立资源工厂之间相互独立线程是工厂中的工人,多个工人协作完成任务工厂内有一个或多个工人工人之间共享空间

请牢记,浏览器的渲染进程是多线程的。主要有以下几个线程

GUI渲染线程

负责渲染浏览器界面,解析HTML,CSS,构建DOM树和RenderObject树,布局和绘制等。
当界面需要重绘(Repaint)或由于某种操作引发回流(reflow)时,该线程就会执行

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

JS引擎线程

也称为JS内核,负责处理Javascript脚本程序。(例如V8引擎)

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

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

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

事件触发线程

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

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

当对应的事件符合触发条件被触发时,该线程会把事件添加到待处理队列的队尾,等待JS引擎的处理
注意,由于JS的单线程关系,所以这些待处理队列中的事件都得排队等待JS引擎处理(当JS引擎空闲时才会去执行)

定时触发器线程

传说中的setInterval与setTimeout所在线程

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

因此通过单独线程来计时并触发定时(计时完毕后,添加到事件队列中,等待JS引擎空闲后执行)
注意,W3C在HTML标准中规定,规定要求setTimeout中低于4ms的时间间隔算为4ms。

异步http请求线程

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

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

如果你了解 web worker ,那么 worker 线程也是其中之一。

可以参考这篇文章了解各个线程的作用:连接

注意:GUI渲染线程,JS引擎线程是互斥的!!!

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

JS在执行的过程中页面是停止渲染的!!!

浏览器渲染流程

浏览器内核拿到内容后,渲染大概可以划分成以下几个步骤:

解析html建立dom树解析css构建render树(将CSS代码解析成树形的数据结构,然后结合DOM合并成render树)布局render树(Layout/reflow),负责各元素尺寸、位置的计算绘制render树(paint),绘制页面像素信息浏览器会将各层的信息发送给GPU,GPU会将各层合成(composite),显示在屏幕上。

JS的运行机制

了解浏览器渲染流程,还要了解JS的运行机制。JS的运行机制就是事件循环

JS引擎是单线程,负责执行JS代码的。

JS分为同步任务和异步任务同步任务都在主线程上执行,形成一个执行栈主线程之外,事件触发线程管理着一个任务队列,只要异步任务有了运行结果,就在任务队列之中放置一个事件。一旦执行栈中的所有同步任务执行完毕(此时JS引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中,开始执行。

在进程启动时,便会创建一个类似于while(true)的循环,每执行一次循环体的过程我们称为Tick。每个Tick的过程就是查看是否有事件待处理,如果有,就取出事件及其相关的回调函数。如果存在关联的回调函数,就执行它们。

宏任务与微任务


上图大致描述就是:

主线程运行时会产生执行栈,

栈中的代码调用某些api时,它们会在事件队列中添加各种事件(当满足触发条件后,如ajax请求完毕)

而栈中的代码执行完毕,就会读取事件队列中的事件,去执行那些回调如此循环注意,总是要等待栈中的代码执行完毕后才会去读取事件队列中的事件

再根据线程来理解下:

宏任务都是放在一个事件队列中的,而这个队列由事件触发线程维护

微任务都是添加到微任务队列(Job Queues)中,等待当前宏任务执行完毕后执行,而这个队列由JS引擎线程维护

所以,总结下运行机制:

执行一个宏任务(栈中没有就从事件队列中获取)执行过程中如果遇到微任务,就将它添加到微任务的任务队列中宏任务执行完毕后,立即执行当前微任务队列中的所有微任务(依次执行)当前宏任务执行完毕,开始检查渲染,然后GUI线程接管渲染渲染完毕后,JS线程继续接管,开始下一个宏任务(从事件队列中获取)

如图:

JS哪些API是宏任务,哪些是微任务?

宏任务(macrotask)::

setTimeout、setInterval、postMessage、 MessageChannel、setImmediate

微任务(microtask):

Promise().then()、 MutaionObserver、process.nextTick(Node.js环境)

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。