什么是事件循环机制?
事件循环分为两种,分别是浏览器事件循环
和node.js事件循环
,本文主要对浏览器事件循环进行描述。
我们都知道JavaScript
是一门单线程语言,指主线程只有一个。Event Loop
事件循环,其实就是JS引擎管理事件执行的一个流程,具体由运行环境确定。目前JS的主要运行环境有两个,浏览器
和Node.js
。
先了解浏览器的进程和线程:
浏览器是多进程的,浏览器每一个打开一个Tab页面(网页)都代表着创建一个独立的进程(至少需要四个,若页面有插件运行,则五个)。渲染进程(浏览器内核)是多线程的,也是浏览器的重点,因为页面的渲染,JS执行等都在这个进程内进行
1.GUI渲染线程
负责渲染浏览器界面,包括解析HTML,CSS
,构建DOM
树和RenderObject
树,布局和绘制等。
当界面需要重绘(Repaint
)或由于某种操作引发回流(reflow
)时,该线程就会执行。
注意 : GUI
渲染线程与JS
引擎线程是互斥的。
2.JS引擎线程
也称为JS内核
,负责解析处理Javascript
脚本,运行代码。(例如V8引擎)。
JS引擎一直等待并处理任务队列中任务。一个Tab页中无论什么时候都只有一个JS线程在运行JS程序
3.定时触发器线程setInterval
和setTimeout
所在线程。通过此线程计时完毕后,添加到事件队列中,等待JS引擎空闲后执行
4.事件触发线程
当一个事件被触发时该线程会把事件添加到事件队列,等待JS引擎的处理
这些事件可来自JS引擎当前执行的代码块如setTimeOut
、也可来自浏览器内核的其他线程如鼠标点击、AJAX
异步请求等,但由于JS的单线程关系所有这些事件都得排队等待JS引擎处理。
6.异步http请求线程
在XMLHttpRequest
连接后是通过浏览器新开一个线程请求。
将检测到状态变更时,如果设置有回调函数,异步线程就产生状态变更事件,将这个回调再放入事件队列中。再由JS引擎执行
Event Loop ( 事件循环 )
浏览器的事件循环分为同步任务和异步任务;所有同步任务都在主线程上执行,形成一个函数调用栈(执行栈),而异步则先放到任务队列(task queue
)里,任务队列又分为宏任务(macro-task
)与微任务(micro-task
)。下面的整个执行过程就是事件循环
- 宏任务大概包括::
script
(整块代码)、setTimeout
、setInterval
、I/O、UI交互事件、setImmediate
(node
环境) - 微任务大概包括::
new promise().then
(回调)、MutationObserver
(html5新特新)、Object.observe
(已废弃)、process.nextTick
(node环境)
若同时存在promise
和nextTick
,则先执行nextTick
宏任务与微任务
JavaScript
代码运行时,任务分为宏任务和微任务两种,Event Loop
也将任务队列分为宏队列
和微队列
分别管理宏任务和微任务,宏队列和微队列也具备队列特性:先进先出。
事件循环的进程模型
- 选择当前要执行的宏任务队列,选择一个最先进任务队列的宏任务,如果没有宏任务,则会跳转至微任务的执行步骤。
- 将事件循环的当前运行宏任务设置为已选择的宏任务。
- 运行宏任务。
- 将事件循环的当前运行任务设置为null。
- 将运行完的宏任务从宏任务队列中移除。
- 微任务步骤:进入微任务检查点。
- 更新界面渲染。
- 返回第一步。
只要主线程空了,就会读取任务队列,这就是js的运行机制,也被称为EventLoop
机制。
在同一个上下文中,总的执行顺序:同步代码→微任务→宏任务
将运行完的宏任务从宏任务队列中移除。 微任务步骤:进入微任务检查点。 更新界面渲染。 返回第一步。 只要主线程空了,就会读取任务队列,这就是js的运行机制,也被称为EventLoop机制。 在同一个上下文中,总的执行顺序:同步代码→微任务→宏任务
- 将运行完的宏任务从宏任务队列中移除。
- 微任务步骤:进入微任务检查点。
- 更新界面渲染。
- 返回第一步。
只要主线程空了,就会读取任务队列,这就是js的运行机制,也被称为EventLoop
机制。
在同一个上下文中,总的执行顺序:同步代码→微任务→宏任务
接着打印7。
同步任务执行完成→js执行栈为空→去查找微任务:微任务为空→查到宏任务。
执行第一个宏任务,打印2,接着执行new Promise
,打印3,Promise.then
属于微任务,挂起放到微任务队列中。
宏任务执行完成→js执行栈为空→查找微任务:打印4
继续循环查找宏任务,执行到第二个setTimeout
,执行顺序同上。