前言
在说内容之前,先发一道面试题,内容如下:
1 | //例题1 |
这个题目比较简单,应该能够看出来结果是:script start -> script end -> promise1 -> promise2 -> setTimeout
那为什么是这个顺序呢,结合一些网上的文章,我尝试用自己的理解或者说法去记录这个知识点。
浏览器内核渲染进程介绍
在浏览器的每一个Tab,都被认为是一个渲染进程,渲染进程内部分为多个线程,这多个线程之间相合作,各自管理着各自专注的领域的事情:
- GUI渲染线程
- 渲染页面,计算布局和绘制样式
- 重绘和回流时会执行
- 和JS引擎线程互斥 (防止结果混乱不可预期)
- JS引擎线程
- 解析和执行JS代码
- 单线程
- 和GUI渲染互斥(防止结果混乱不可预期)
- 事件触发线程
- 管理事件的循环,鼠标点击/滚动、setTimeout、ajax
- 在达到条件时,将回调方法放入JS引擎的执行队列
- 定时器触发线程
- setTimeout和setInterval的管理线程
- 定时任务由定时器触发线程计时(如果定时器不多的页面,这个线程岂不是很空闲)
- 当计时完毕,通知事件触发线程
- 异步http请求线程
- 发起和处理异步请求的线程
- 当请求完成,有回调时,通知事件触发线程干活
Event loop机制介绍
首先我们要明确一个点,JS是从上而下执行的,然后遇到异步的方法的时候,再去进行一些处理,这个异步处理的机制就是要去了解的Event loop。
在JS执行过程中,浏览器分配堆内存去存储数据,而JS方法的执行上下文(调用栈)则是如同其名,是用栈结构存储的。
在浏览器JS执行的过程中,遵守一种策略:
- 执行栈里面的任务,遇到异步的任务,先交给对应的线程去处理
- 执行完栈内的任务后,询问事件触发线程,是否有新的回调(来自多处)可执行,有的话,执行栈被重新添加任务执行
- 重复1-2步
那什么是宏任务、什么是微任务呢?
我觉得说这两个之前,得先解释一下浏览器的JS引擎线程跟GUI线程之间的执行顺序先。上面提到这两个线程是互斥的,那么就有一个机制,让他们能够有序地执行。这个机制,就是轮流…对就是这么简单。JS引擎执行完,轮到GUI引擎执行一下,如此往复:JS -> GUI -> JS -> GUI。那么又有新的问题了,JS引擎怎么才叫执行完一次呢?其实就是上面Event loop机制提到的第一步执行完了就算一次执行完。
一个执行栈的同步执行的代码,被认为是宏任务
。
eg1:
1 | document.body.style = 'background:black'; |
eg2:
1 | document.body.style = 'background:blue'; |
那说完宏任务,什么是微任务?
微任务
是在宏任务执行指挥立即执行的任务。包括Promise.then process.nextTick
微任务在宏任务和GUI之间执行。所以上面的JS -> GUI -> JS -> GUI流程可以改成:宏任务 -> 微任务 -> GUI -> 宏任务 -> 微任务 -> GUI ···
eg3:
1 | document.body.style = 'background:blue' |
图示: