Event Loop 事件循环
Event Loop的作用是什么?
回答:Event Loop
规定了JavaScript
中的事件的执行顺序,解决了JavaScript
单线程运行阻塞问题。
什么是单线程?
回答:单线程指的是一个程序或进程在任何时刻只有一个线程在执行。
为什么JavaScript
是单线程运行?
回答:因为JavaScript
涉及的初衷是用来作为一种脚本语言执行,处理简单的业务逻辑和用户交互。单线程符合当时的设计需求和技术限制。
Event Loop的执行机制是什么?
在单线程的执行模型中,任务(或数据项)按照它们被提交到执行队列的时间顺序进行排队,并遵循“先进先出”(FIFO, First In First Out)的原则进行处理。具体结构如下:
回到本问题,事件循环的执行流程是:宏任务(macrotask) > 微任务(microtask) > 界面渲染(GUI)
- 执行宏任务(Macrotask) :宏任务是较大的任务单元,通常表示一整块代码的执行,如
setTimeout
、setInterval
、I/O
操作等。 - 执行微任务(Microtask) :微任务是更小、更精细的任务单元,常用于处理细粒度的操作,如
Promise.then
、MutationObserver
等。在宏任务执行完毕后,会立即执行所有已注册的微任务。 - 渲染界面(GUI) :在所有宏任务和微任务执行完成后,浏览器会进行页面的渲染更新。
案例解析
下面我们开始使用案例来解析事件循环,有下面一段代码:
<script>
setTimeout(function () {
console.log(1);
}, 0);
new Promise(function (resolve, reject) {
console.log(2);
for (var i = 0; i < 10; i++) {
resolve();;
}
console.log(3);
setTimeout(function () {
console.log(4);
}, 0);
}).then(function () {
console.log(5);
});
console.log(6);
setTimeout(function () {
console.log(7);
}, 0);
</script>
代码解析:
- 整个
<script>
标签内的代码块被视为一个宏任务,其中的同步任务会被马上执行,所以输出结果是2,3,6
。 - 当前宏任务中的所有同步代码执行完毕后,检查并执行所有微任务,比如:
promise.then
,所以接下来的输出结果是5
。 - 宏任务中的异步任务会开启新一轮的事件循环,异步任务内部的代码将作为宏任务执行。所以接下来会开启三轮新的事件循环,并在宏任务的流程中依次输出是
1,4,7
。
具体的执行顺序如下:
使用浏览器的performance
功能进一步验证我们的猜想是正确,具体结果如下:
总结:
-
第一轮事件循环:
- 宏任务:
2, 3, 6
- 微任务:
5
- 宏任务:
-
第二轮事件循环:
- 宏任务:
1
- 宏任务:
-
第三轮事件循环:
- 宏任务:
4
- 宏任务:
-
第四轮事件循环:
- 宏任务:
7
- 宏任务:
异步编程
什么是异步编程?
异步编程是一种编程模式,它允许程序继续执行而不必等待某个长时间运行的操作完成。常见于客户端与服务器的交互,客户端可以在等待服务器响应的同时继续处理其他任务。这种模式提高了程序的效率和响应性,特别是在处理I/O操作、网络请求等耗时操作时。
单线程如何实现异步编程?
JavaScript通过引入任务队列(Event Queue)和事件循环(Event Loop)来实现异步编程。异步任务被放入任务队列中,主线程在完成当前任务后,会检查并执行队列中的异步任务。任务队列的数据结构是先进先出(FIFO)的队列,用于管理待处理的任务。
任务队列的数据结构如下:
什么是同步任务和异步任务?
- 同步任务:立即执行的任务代码,主线程需要等待任务完成后才能继续执行后续代码。
- 异步任务:延迟执行的任务代码,主线程可以继续执行其他代码,异步任务在完成后通过回调、Promise、
async/await
等方式处理结果。
异步任务通常会被放入任务队列中,主线程在处理完当前同步任务后会检查并执行队列中的异步任务。