事件循环
基础概念: 进程和线程.
什么是进程
程序是指令.
进程是程序的实体.
当我们打开电脑,查看任务管理器,可以看到每当打开一个新的应用软件,在任务管理器中就会看到对应的进程.
如果我们结束了对应的进程,就等同于关闭了对应的应用软件.
主进程可以打开新的子进程.

所以通俗的描述,进程就是程序在运行时所产生的实体,是系统进行资源分配和调度的基本单位.
什么是线程
线程是进程中实际执行任务的单位

进程和线程的关系
进程是线程的容器
比喻
假如把一台电脑的操作系统比作一家大公司.
那么可以把一个进程看作为公司内部的一个部门.
而线程就是部门内部真正在干活的员工.
当我们在任务管理器中将某个进程结束掉了,就相当于我们在大公司中把整个业务部门裁掉了,对应的里面的员工也会全部被释放.
概述
什么是事件循环
事件循环就是一个执行消息队列的机制
为什么会出现事件循环
结论: 因为浏览器的渲染主线程是单线程,而为了避免主线程出现阻塞的情况,采用了异步的形式执行任务.为了解决异步任务的问题,最终采用事件循环这种执行消息队列的机制.
单线程是异步产生的原因
事件循环是为了解决异步的机制
浏览器进程模型
浏览器是一个复杂的程序,其内部的复杂程度已经不亚于一个操作系统
使用shifit + Esc
即可打开浏览器的任务管理器,内部可以看到一个浏览器存在的各个进程.


一个浏览器中,会同时存在多个进程,每个进程分管不同的功能以及业务.用户打开每个页面,又会新增一个页面渲染进程.
而我们的事件循环机制就是为了解决渲染主线程的消息队列而存在的,今天探讨的主要对象就是渲染主进程的机制
渲染主进程
渲染主进程是浏览器页面进程中最主要的进程
渲染主进程是干什么的
渲染主进程主要有
- 解析HTML
- 解析JS
- 解析CSS
- 构建DOM树
- 构建Render树
- 布局绘制等渲染浏览器界面
- 脚本执行
- 事件处理
渲染主进程为什么是单线程
由上可见,渲染主进程需要处理非常多的任务,那为什么不把这个进程设计为多线程呢?
因为浏览器从收到HTML JS CSS 等资源到最终呈现画面给用户这个过程中,很多的任务是需要按照一定的顺序进行执行的,因为环环相扣,所以没办法使用多线程.
后文对渲染主进程的单线程统称为 渲染主线程
单线程会带来什么问题
会带来程序阻塞
主线程在处理到不能立刻执行完毕的任务时(比如延时任务,比如网络请求等任务),如果停留在当前任务等待任务执行完毕,就会使得后面所有的任务阻塞,从而使得页面出现卡死的状态.
比喻
如果把一个浏览器应用中的一个页面渲染过程,比作是一家餐厅,那么渲染主线程就相当于餐厅的服务员.他需要接待客人之后,为客人点菜,并将菜单告知后厨,让厨师制作出对应的菜品供顾客食用.
如果采用等待延时任务的模式,那么这家餐厅的服务员就会接待客人,并且把菜单告知厨师之后,会在原地等待厨师做好饭菜,并且会在原地等候客人吃饱结账离开,才会继续接待第二个客人.这样的模式就会让其他客人干等,极大了浪费了效率.别的客人在等待的同时,就是程序出现卡死的状态.
如何解决单线程带来的阻塞问题
放弃等待,先做下一个
在主线程遇到延时任务时,主线程会立即结束该任务,并且告知计时线程.随后从消息队列中获取下一个任务继续执行.
计时线程在接到主线程派发的任务后,就会默默计时,一旦计时结束,计时线程会将刚才为执行完毕的任务再重新包装成一个队列任务,重新放回消息队列中.这时候主线程再次遇到这个任务时,就已经可以直接执行完毕了.
比喻
还是餐厅和服务员的比喻,这次的模式就是服务员在帮第一桌客人下单以后,就把让厨师开工,厨师自己把菜品做好以后,就告知服务员过来送菜.这样就避免了服务员原地干等,服务员在厨师做菜的期间,可以继续接待其他的客人,极大提升了工作效率.
浏览器中的任务如何产生

在主线程执行任务的过程中,自己有可能也会产生任务进入任务列表排队
比如在fn1函数内,使用JS在代码中进行了模拟点击事件,则这个点击事件就会被生成,进入任务队列中
后文会把任务队列称为消息队列
待处理任务有优先级吗?
必须有优先级!
刚刚我们已经了解了浏览器中存在一个消息队列用于存放待执行的各种任务,那么这么多的任务,有没有有优先级呢?
消息队列里的任务优先机制,就事件循环
事件循环的优先级是如何区分的
在浏览器中的消息队列中,存在多个不同的队列,分别存储不同类型的任务

比较常见的几个队列是
- 微队列 优先级:高
- 用户交互队列 优先级:中
- 定时任务队列 优先级:低
在JS中如何让任务立马放进微队列
Promise.resolve().then(()=>{
// 需要执行的代码
})
消息队列以什么顺序获取任务
当消息队列空了以后,会依据优先级高低的顺序,依次询问微队列,用户交互队列,以及定时任务队列是否有任务,一旦有任务,则这个任务则会被送入消息队列等待执行.如果微队列没任务,则开始查看用户交互队列,以此类推.
这就是浏览器的事件循环机制
代码演示
了解了事件循环机制以后,我们就可以很好的理解为什么在JS代码中会出现很多比较迷惑的代码,例如
function messageLoop () {
// 1. 主线程运行定时任务时, 立即把 consolg.log(1) 放入定时队列,然后结束当前任务, 开始执行下一行 此时控制台无输出
setTimeout(()=>{
console.log(1);
},0)
// 2. 主线程执行到这里的时候,此任务不属于定时任务,可以直接执行完毕,此时控制台输出 // 3
console.log(3);
// 3. 主线程执行到这里的时候,产生了一个微队列任务, 会把 consolg.log(2) 放入微队列,但是不执行 此时控制台无新增输出
Promise.resolve().then(
console.log(2)
)
// 4. 此时主线程已经把消息队列里面的任务都执行完毕,这时候会依据 微队列 > 用户操作队列 > 延时队列 的优先级,从微队列和延时队列中取任务
// 5. 先取出微队列的 consolg.log(2) 运行. 控制台输出 // 2
// 6. 微队列已经执行完毕, 开始从延时队列找取出 consolg.log(1) 运行, 控制台输出 // 1
// 7. 所有任务执行完毕,控制台输出顺序为 3 2 1
}
messageLoop() // 3 2 1
结语
好了,扯皮的话讲完了, 这就是关于消息队列的一些简单理解了.这回如果再有面试官问你的时候, 你就可以一顿秀了
Q:
请说说你对JS事件循环的理解
A:
- 首先事件循环是浏览器渲染主线程一个执行消息队列的机制
- 这个机制存在的原因是为了解决浏览器主线程运行异步任务阻塞的问题.
- 在浏览器中消息队列中常用的几种队列大致分为 微队列 用户操作队列 延时队列 . 在过去的浏览器仅仅分为微队列和宏队列,但是对于今天的浏览器,仅仅区分2个队列已经无法满足当前的前端发展,W3C已经明确表示已经抛弃过去宏队列的说法,改为更具体的队列.
- 渲染主线程根据自己消息队列中的优先级,按照一定的顺序取出里面的任务,这就是事件循环机制.其中微队列优先级最高,其次到用户操作,然后到延时队列.
秀一下我的猫

拜拜,希望我的笔记可以帮助到你.
网友评论