浏览器事件循环介绍
浏览器是单线程的,代码在执行栈(Call Stack)中执行,这意味着它在任何时候只能执行一个任务。然而,这并不意味着浏览器无法处理多个任务。为了实现这一点,浏览器引入了事件循环(Event Loop)机制。浏览器中有很多操作是异步的,比如网络请求、定时器、用户交互等。浏览器异步处理完这些任务,将执行结果放到回调任务队列(Queue),事件循环机制会不断地检查任务队列,当执行栈空闲时,如果队列中有任务,就取出任务并执行,然后再检查下一个任务,如此循环往复,直到任务队列为空。

宏任务(macrotask)和微任务(microtask)
在浏览器事件循环中,任务被分为两种:宏任务(macrotask)和微任务(microtask),相应的有两个任务队列。宏任务包括像setTimeout、setInterval、setImmediate、I/O操作等,这些任务会被放入宏任务队列。而微任务包括Promise、MutationObserver、queueMicrotask等,这些任务会被放入微任务队列。

在每一次事件循环中,浏览器首先会执行一个宏任务,然后执行所有的微任务。也就是说,微任务的优先级高于宏任务。如果在执行宏任务的过程中产生了微任务,那么这些微任务会在当前宏任务结束后,下一个宏任务开始前被执行。这样可以保证更快的响应,因为微任务通常用于处理一些更紧急的、需要快速响应的操作。
javascript
// 宏任务、微任务执行流程
while (true) {
macrotask = macroqueue.pop();
execute(macrotask);
// 需要执行完微任务队列的任务再进入下个循环
while (microtask.hasTask()) {
doMicrotask()
}
}
代码示例分析
下面通过代码示例来验证宏任务和微任务的执行流程
示例1:
javascript
// 宏任务
setTimeout(function macrotask1() {
console.log("宏任务1");
}, 0);
// 微任务
Promise.resolve().then(function microtask1() {
console.log("微任务1");
});
// 微任务
Promise.resolve().then(function microtask2() {
console.log("微任务2");
});
// 宏任务
setTimeout(function macrotask2() {
console.log("宏任务2");
}, 0);
// 输出结果
console.log("同步任务");
// 输出顺序:
// 同步任务
// 微任务1
// 微任务2
// 宏任务1
// 宏任务2
通过Chrome的Performance可以看到,在执行下一个宏任务之前,会先执行微任务队列。两个timer宏任务1和宏任务2在接下来的事件循环中依次执行。

示例2:
javascript
console.log('开始');
setTimeout(function macrotask1() {
console.log('宏任务1');
Promise.resolve().then(function microtask1() {
console.log('微任务1');
});
}, 0);
Promise.resolve().then(function microtask2() {
console.log('微任务2');
setTimeout(function macrotask2() {
console.log('宏任务2');
}, 0);
}).then(function microtask3() {
console.log('微任务3');
});
console.log('结束');
// 输出顺序:
// 开始
// 结束
// 微任务2
// 微任务3
// 宏任务1
// 微任务1
// 宏任务2
由图可见,在第一个任务循环中执行了微任务2和3,在第二个循环中执行宏任务1,因为宏任务1中创建了微任务1,所以会在当前循环中执行微任务1,最后再到下一个循环执行宏任务2

为什么需要微任务
微任务是更高优先级的任务队列,提供了一种事件插队的机制,在一次事件循环中,会先执行当前的所有微任务,然后再执行下一个宏任务。这样可以确保微任务在宏任务之前执行,避免了因为队列中有很多任务时导致执行阻塞,数据没有及时更新的问题。
例如,我们通过Promise实现异步数据获取,在then方法中更新UI,这样在保证异步编程的同时,UI也得到了及时的更新。
javascript
fetchData().then((data) => {
updateUI(data);
});
总结
本文介绍了浏览器的事件循环机制以及宏任务和微任务的区别。总的来说,宏任务和微任务是前端开发中处理异步操作的基础,浏览器的事件循环机制通过宏任务和微任务,实现了同步和异步操作的统一调度。
了解宏任务和微任务的执行顺序,可以帮助我们更好地理解和使用异步编程模式,并且更好地理解和预测你的代码行为。通过合理地使用宏任务和微任务,我们可以优化代码的性能和响应速度,提高应用程序的稳定性和可维护性。