事件循环机制
javaScript是运行于浏览器环境的一门编程语言,其具有单线程、异步的特性。要想更好的去理解,就需要明白事件循环机制。
事件循环机制是浏览器为了适应javaScript这门语言的特性而内置的一种机制。
要弄明白这种机制,我们需要先强调这几个概念: 单线程、异步、执行队列、浏览的进程和线程
什么是单线程
先来说说什么是单线程吧,其实也很好理解!javaScript代码执行的时候需要有一个执行线程去做解析执行代码这件事,浏览器中只有一个执行线程。这也就解释了什么是单线程。
有的小伙伴就问了,那不能多设置一个执行线程吗?当然可以,如果再设置一个线程,那么理论上是可以实现两个线程一起去解释执行javaScript代码的。这样效率明显是提高的,但是为什么不这么做呢?
这就是另一个问题了,为什么javaScript要设置成单线程?
为什么javaScript是单线程的
想一想,浏览器中运行的javaScript的主要作用是干什么的?对,做视图以及交互的。那么不可避免的需要使用javaScript去操作dom。如果有两个线程在运行javaScript的话,那么会让界面变的不可控制。 举个例子:假设线程1把div.box的宽度变成了100px,然后基于100的宽度需要做一些操作。但是,另一个线程又将div.box设置成了200px,那这样的话视图就乱套了,写的程序也会出现问题。
想想就很可怕,但是幸好
javaScript是单线程。
什么是异步
单线程的程序有个明显的问题,相信大家也发现了,那就是效率比较低,尤其是碰见了一些任务比较耗时的时候,效率问题就非常明显。为了解决这个问题,就引入了异步的概念。 所谓异步的概念就是,有些任务是耗时的但是又不占用js执行线程,这一类任务我们可以先将其挂起,等需要占用js执行线程的时候再放入执行队列。这样,整个程序的执行效率就会得到保障了。 所以说,在单线程的前提下,异步可以保障程序的执行效率。
浏览器的进程和线程
浏览器是多进程的,主要如下:
- Browser进程(了解)
- 浏览器的主进程(负责协调、主控),该进程只有一个。
- 负责浏览器界面显示,与用户交互。如前进,后退等
- 负责各个页面的管理,创建和销毁其他进程
- 讲渲染(Renderer)进程得到的内存中的Bitmap(位图),绘制到用户界面上
- 网络资源的管理,下载等
- 第三方进程
- 每种类型的插件对应一个进程,当使用该插件时才创建
- GPU进程
- 该进程也只有一个,用于3D绘制等等
- 渲染进程(重要)
- 即通常所说的浏览器内核(Renderer进程,内部是多线程)
- 每个Tab页面都有一个渲染进程,互补影响
- 主要作用为页面渲染,脚步执行,事件处理等
Renderer进程的主要线程
Renderer进程主要负责页面的渲染,JS的执行,事件的循环,该进程下有多个线程:
- GUI渲染线程
- js引擎线程
- 事件触发线程
- 定时器触发线程
- 异步http请求线程
其中 GUI渲染线程和js引擎线程是互斥的,也就是说当GUI渲染线程执行的时候,那么js引擎线程肯定是不执行的,同样的js引擎执行的时候,GUI渲染线程也是停止的。
js引擎线程是维护着两个执行队列(宏任务队列和微任务队列)的,js引擎线程会依次执行队列里的任务的,等一个队列执行结束会去执行另一个队列。
所以,js引擎线程和GUI渲染线程的交替执行顺序如下:
js引擎执行宏任务队列 -> js引擎执行微任务队列 -> GUI进行渲染任务 -> js引擎执行宏任务队列 -> js引擎执行微任务队列 -> GUI进行渲染任务 ....
宏任务和微任务
常见的宏任务
- 主代码块
- setTimeout
- setInterval
- setImmediate - node
- requestAnimationFrame
常见的微任务
- process.nextTick - Node
- Promise.then
- catch
- finally
一道面试题
通过下边的面试题,测测自己是否掌握了事件循环
// 请说出输出顺序并说明原因
async function async1() {
console.log('async1 start');
await async2();
console.log('async1 end');
}
async function async2() {
console.log('async2');
}
console.log('script start');
setTimeout(function () {
console.log('setTimeout');
}, 0)
async1();
new Promise(function (resolve) {
console.log('promise1');
resolve();
}).then(function () {
console.log('promise2');
});
console.log('script end');
