简言
记录下web的事件循环解释。
事件循环
前置知识
网页进程
现代浏览器是多进程架构浏览器,一个标签是一个进程,进程有多个线程。
一个网页标签进程主要有以下线程:
- 主线程:除了运行网页本身的代码之外,还负责收集和派发用户和其他事件,以及渲染和绘制网页内容等。
- 合成线程:合成线程负责将渲染树转换为图层,并执行图层的合成操作,负责页面的滚动和动画等操作,可以在不需要主线程参与的情况下独立完成部分工作,以提高性能。
- GPU 线程:处理 GPU 任务,例如 WebGL 或者 CSS 3D 变换等。
- I/O 线程:处理磁盘和网络 I/O。
- 定时器线程:setTimeout、setInterval的计时在这上面进行。
- worker线程(Worker Thread):执行 Web Worker 或 Service Worker 的代码。
任务和微任务
哪些任务属于任务?
(1)整体的 script 代码(也就是一开始的全局代码)
(2)setTimeout
和 setInterval
的回调
(3)setImmediate
的回调(Node.js 环境)
(4)I/O 操作的回调函数(主要是早期,I/O 操作通常使用回调函数来处理异步结果。现代JavaScript中不管是浏览器的网络请求,还是nodejs的网络请求和文件读写,普遍返回promise,因此属于微任务)
(5)用户交互事件(如 click、keydown 等)
(6)UI 渲染更新
(7)postMessage、MessageChannel
(8)WebWorker 的 message 事件
哪些任务属于微任务?
(1)Promise
的 then
和 catch
的回调
(2)process.nextTick
的回调(Node.js 环境)
(3)MutationObserver
的回调
(4)queueMicrotask
方法的回调
(5)async/await
(实际上是通过 Promise
实现的)
为什么要有微任务?你可以理解为,都是回调,但是有一些的优先级要比其他的更高,所以被单独放入了一个队列里,并把这些任务定义为微任务。所以微任务队列调度优先级高。
解释
web的事件循环是一种解释执行网页代码的机制。
**事件循环负责收集事件(包括用户事件以及其他非用户事件等)、对任务进行排队以便在合适的时候执行回调。**然后它执行所有处于等待中的 JavaScript 任务,然后是微任务,然后在开始下一次循环之前执行一些必要的渲染和绘制操作
事件循环是在主线程中运行的。
类似于:
while (queue.waitForMessage()) {
queue.processNextMessage();
}
事件循环驱动着浏览器中发生的一切,因为它与用户的交互有关,但对于我们这里的目的来说,更重要的是它负责调度和执行在其线程中运行的每一段代码。
一次事件循环的流程:
- 执行同步代码(包括一开始的全局代码,或者后面从任务(也可以叫宏任务)队列取出的同步代码。)
- 执行微任务队列的代码,清空微任务队列(体现优先级高)。
- 尝试进行和绘制(不一定立刻重新渲染,浏览器为了提高渲染效率,可能会把几次渲染合并进行。)。
- 从任务队列中取出下一个任务,进入下一次事件循环。
案例
Promise.resolve()
.then(() => { // then1
console.log(0);
return Promise.resolve(4); // then2
})
.then((res) => { // then4
console.log(res);
});
Promise.resolve()
.then(() => { // then1
console.log(1);
})
.then(() => { // then2
console.log(2);
})
.then(() => { // then3
console.log(3);
})
.then(() => { // then5
console.log(5);
})
.then(() => { // then6
console.log(6);
});
都是微任务,结果:
0,1,2,3,4,5,6
流程:
-
then1先进微任务队列 , 结果输出 0,1 ,将Promise.resolve(4)的promise对象推入微队列,
-
then2 进入微队列, 输出2
-
将return Promise.resolve(4)返回的promise值(还是promise对象)推入微队列
-
then3 进入微队列 输出 3
-
then4 进入微队列 输出 4
-
then5 进入微队列 输出 5
-
then6 进入微队列 输出 6
结语
结束了。