微任务和宏任务的讲解, 希望能帮助到你, 戳一戳跳转
(https://blog.csdn.net/weixin_48524561/article/details/124936449)
一、首先说一说什么是宿主环境?
比如: 寄生虫, 它必须寄生在一个生物的体内; 离开了它就活不下去.
js代码就是必须寄生在宿主环境里面的, 离开了宿主js代码就运行不了
那么前端常说的宿主环境有哪两种:
-
浏览器
-
nodejs
当我们在面试的过程当中被面试官问道 “小伙子, 说一说JavaScript的事件循环”. 那么这个面试官的水平可能还不如你.
因为
事件循环并不是针对JavaScript的
, JavaScript中压根就没有事件循环.
只有在
宿主环境中才会有事件循环
的问题. 所以说这个面试官大概率是想问你 “你来说一说宿主环境中的事件循环”.
二、为什么会有事件循环机制?
JavaScript语言最大的特点就是单线程, 简单的来说就是:"同一时间, 只能做一件事情", 如果有很多事情同时需要执行的话, 就需要排队.
打个比方: 在DOM中同时操作两个线程, 一个线程用来删除某个节点上的元素; 另外一个线程用来在这个线程上添加元素. 你觉得浏览器应该先执行谁了?
所以, 事件循环机制就从此诞生了. 只有等删除操作的线程执行完毕之后, 下一个线程才能执行. 但是如果
一个线程的执行时间太长
的话, 就会
造成拥堵
. 这样
事件循环机制就是为了来解决这个事情
的.
三、拆分里面的原理来说一说事件循环
事件循环 = 事件 + 循环.
- 事件就是js中的事件类型, 如: 点击事件, 鼠标移入移出事件, 聚焦事件, 失去焦点事件等等.
- 循环就是一直重复的做, js中的while循环, for循环
这里主要说一说浏览器的事件循环, 以前浏览器给我的感觉就是, 可以运行代码; 面向百度编程, 是不会就搜哪里.
其实浏览器是有很多事情要做的:
- 执行js代码
- 请求数据, 接收响应数据
- 解析css
- 渲染页面
- 鼠标的某某某事件等等
但是浏览器是如何去实现的, 其实在浏览器的内部有很多的模块; 每一个模块后面的其实就是一个进程. 也就是说一个模块就是一个进程.
1.1、什么是进程, 什么又是线程?
进一步把进程进行划分:
-
主进程
: 用来协调控制其他子进程。 -
GPU进程
: 用于3D绘制等。 -
渲染进程
: 就是我们说的浏览器内核,负责具体页面的渲染,脚本执行,事件处理等。浏览器的每个tab页背后就有一个渲染进程。
进程这个单位还是比较大,它进一步拆分多个线程
。可以理解为一个页面上的事还是比较多,要多找些小弟来完成。具体来说,一个渲染进程包括:
4.
主线程
: 统一调度其他线程
5.
GUI渲染线程
: 负责渲染页面,布局和绘制。与JS引擎互斥。
6.
JS引擎线程
: 负责处理解析和执行javascript脚本程序。与GUI渲染线程互斥的
7.
事件触发线程
: 用来控制事件循环(鼠标点击、setTimeout、ajax等)。当事件满足触发条件时,将事件放入到JS引擎所在的执行队列中。(
为什么你触发的事件可以被捕捉到, 是因为此线程在监听
)
8.
setInterval与setTimeout所在的线程
: 定时任务并不是由JS引擎计时的,是由定时触发线程来计时的。计时完毕后,通知事件触发线程。(
其实定时器并不是ECMA所规定的的, 而是宿主环境给予的
)
9.
异步http请求线程
: 浏览器有一个单独的线程用于处理AJAX请求,当请求完成时,若有回调函数,通知事件触发线程。
10.
io线程
: 用来接收其他进程的消息。
每个渲染进程(一个tab页)都有一个主线程,并且主线程非常繁忙,既要处理 DOM,又要计算样式,还要处理布局,同时还需要处理 JavaScript 任务以及各种输入事件(但是如果在JS引擎线程里面写一个死循环的话, 后面的事情都得堵着)。
浏览器又是如何的去有条理的去处理这些
不同的类型的任务在主线程里面执行,
这个时候就
需要一个系统来调度
它们, 指挥它们.
这个调度的系统就是消息队列和事件循环
.
1.2、什么是消息队列, 什么又是事件循环?
任务有很多,人只有一个,且任意时刻只能做一件事(不是一边走路一边听课这种事哈),那怎么办,就是排队呗
简单的理解就是一个教室只有一个老师, 但是会有很多的学生,
学生们同时的向老师提出不同的问题, 老师是不可能一下全部的解决掉的,
而是拿一个小纸条放在讲台上, 学生们把自己的问题写在小纸条上面; 老师解决了当前学生的问题就会优先的解决下一个学生的问题,
当所有学生的问题全部被解决完毕之后, 老师也就闲下来了; 如果哪个学生还有问题就举手, 老师就再去解决,
老师就这样一直循环着, 有问题就解决; 没有问题就等待着问题, 再去解决问题.
消息队列就好比是, 同时出现问题; 就得排队. 先来的先做
事件循环就好比是, 同一时间没有问题了, 老师闲下来了; 学生一有问题老师就去解决, 一直循环.
- 主线程上要做很多事情,例如:js代码执行,页面布局计算,渲染等
- 主线程同一时刻只能做一件事,事情多了就要排队。所以主线程维护了任务队列。
- 某个事件发生时,事件触发线程 就把对应的任务添加到主线程的任务队列中。
- 主线程上的任务完成之后,就会从任务队列中取出任务来执行。
四、总结
当面试官问道, 解释一下宿主环境中的事件循环, 可以这样说:
- 任务是以事件以及回调1的方式存在的(如: onclick(回调函数))
- 当事件(用户的点击, 图片的成功加载)发生时, IO线程就会将其回调添加到任务队列中
- 当主线程中的任务完成之后, 就会以从任务队列中取出任务来执行
- 此过程不端的重复的去执行, 就会形成一个循环; 此循环称之为eventLoop
- 抛开前端, 浏览器不谈, 说一个更广的层面; 在程序世界里面, 事件循环机制是很常见的. 也就是事有很多, 但是干活的只有一个, 那就一定要排队; 并且还有一个机制去指挥它们.