1. 为什么需要事件队列
js是单线程语言,一次性只能执行一个任务。而有些任务比较耗时,如网络请求、定时器等,会阻塞后续任务进行。因此,需要一种异步解决方案,尽可能使应用可以流畅运行--事件队列,或者说消息队列。
2. 什么是事件队列
事件队列,就是浏览器处理多任务的异步解决方案。
它会把任务划分到各个队列之中,依次执行。
它可以帮助我们优化性能,将耗时的任务放在队列中,在空闲的时候执行,以免阻塞主线程。
3. 事件队列的处理方式
以Chrome浏览器为例,每个标签页都是一个独立的进程。而该进程在处理任务时,会有一个主线程。主线程会处理包括渲染页面,事件交互等诸多任务。为了防止事件阻塞,浏览器会把各个任务分配到不同的事件队列中,而不同的事件队列,有不同优先级。
- 微任务队列:主线程完成后会立即执行的任务队列,通常是异步任务的回调, 如Promise和MutationObserver 的回调。浏览器会将回调函数包装成任务,插入微队列中。优先级高。
- 宏任务队列:存储需要异步执行的任务,如定时器、用户交互、I/O操作等。
任务队列遵从先进先出的原则。即任务按照被添加到队列中的顺序来进行执行。
那么,事件队列是怎么运作的呢?
function Fn () {
console.log(1)
}
setTimeout(() => {
console.log(2)
},0)
console.log(3)
new Promise((resolve, reject) => resolve()).then(() => Fn())
以上述代码举例,浏览器拿到js开始执行,主线程开始分析当前任务并划分队列。
1. 主线程执行同步任务
2. 将产生的新任务划分到不同队列
3. 继续执行同步任务,直至主线程中同步任务全部执行,等待下一个同步任务到来
4. 开始执行微任务
5. 渲染与更新
6. 重复步骤3-5,直至队列清空
7. 开始执行宏任务
8. 重复以上步骤,直至所有任务清空,进入等待状态
以上步骤会一直循环进行,也就是事件循环
因此,上述代码输出应为
3 1 2
到这里,事件队列处理机制就很清晰了,所以我们再加一丶丶难度。
function Fn (x) {
console.log(x)
}
new Promise((resolve, reject) => resolve()).then(() => {
console.log(1)
new Promise((resolve, reject) => resolve()).then(() => Fn(2))
})
setTimeout(() => {
console.log(3)
new Promise((resolve, reject) => resolve()).then(() => Fn(4))
}, 0)
console.log(5)
new Promise((resolve, reject) => resolve()).then(() => {
console.log(6)
setTimeout(() => Fn(7), 0)
})
所以,这里输出的结果会是多少呢?