目录
介绍
事件循环(event loop)是一种用于处理异步操作的编程模型。在 JavaScript 中,事件循环是指浏览器或 Node.js 运行时环境中负责管理执行顺序的机制。
首先要知道
- JS是单线程
- 不能同时执行多个任务
- 需要调度异步任务
因此,浏览器/Node提供了任务队列/调度机制/事件循环去解决上面问题。
总结来说,就是因为 JavaScript 是单线程的,事件循环是其调度异步任务的机制。
同步任务与异步任务
同步任务:同步任务是指进入调用栈后立即执行、必须等当前任务执行完成后才能执行下一个任务的代码
(程序按照顺序依次执行,每个操作都要等待上一个操作完成后才能进行)
javascript
console.log(1);
console.log(2);
console.log(3);
执行顺序严格是:
1
2
3
异步任务:异步任务是指不会立即进入调用栈执行,而是交给宿主环境处理,等满足条件后再进入任务队列,等待事件循环调度执行的任务。
(程序中的操作可以同时进行,不需要等待上一个操作完成才能进行下一个操作)
javascript
console.log(1);
setTimeout(() => {
console.log(2);
}, 0);
console.log(3);
//输出
1
3
2
在这里setTimeout 不是立即执行,它交给浏览器计时器模块,到时间后进入宏任务队列,当前宏任务结束后才执行
宏任务与微任务是什么
宏任务
常见:
-
setTimeout
-
setInterval
-
DOM 事件
-
script 整体代码
- 比如以下这种,会把整个代码块当成一个宏任务执行
javascript<script> console.log('hello'); </script> -
setImmediate(Node)
微任务
微任务的优先级会比宏任务的优先级更高
常见:
- Promise.then
- queueMicrotask
- MutationObserver
- process.nextTick(Node)
宏任务与微任务是如何工作的(事件循环的执行顺序)
① 执行一个宏任务(比如 script)
script 整体代码就是第一个宏任务
② 在执行宏任务过程中:
- 同步代码直接进入调用栈执行
- 遇到微任务 → 加入微任务队列
- 遇到宏任务 → 加入宏任务队列
③ 当前宏任务执行结束
④ 清空所有微任务队列(一次性清空)
如果微任务里又产生微任务,会继续执行,直到清空。
⑤ 浏览器可能进行一次渲染
⑥ 从宏任务队列中取出下一个宏任务执行
⑦ 重复以上流程
案例
案例1:请解释最后的打印顺序
javascript
console.log(1);
Promise.resolve().then(() => {
console.log(2);
setTimeout(() => {
console.log(3);
}, 0);
});
setTimeout(() => {
console.log(4);
new Promise((resolve) => {
console.log(5);
resolve();}).then(() => {
console.log(6);
});
}, 0);
console.log(7);
//输出:
//1,7,2,4,5,6,3
步骤分析
javascript
// step 1执行后 输出1 7
微任务 = [
(() => {
console.log(2);
setTimeout(() => {
console.log(3);
}, 0);
})
]
宏任务 = [
(() => {
console.log(4);
new Promise((resolve) => {
console.log(5);
resolve();}).then(() => {
console.log(6);
});
})]
// 步骤2 清空微任务队列 输出 2, 将setTimeout加入宏任务
微任务 = [
]
宏任务 = [
(() => {
console.log(4);
new Promise((resolve) => {
console.log(5);
resolve();}).then(() => {
console.log(6);
});
}),
(() => {
console.log(3);
})]
// 步骤3 取出宏任务第一个执行, 输出4 以及5,并且将then加入微任务队列
微任务 = [
(() => {
console.log(6);
});)
]
宏任务 = [
(() => {
console.log(3);
})]
// 步骤四 清空微任务,输出6
微任务 = [
]
宏任务 = [
(() => {
console.log(3);
})]
//步骤5 清空宏任务,输出3
//最后输出结果
//1,7,2,4,5,6,3
总结
JavaScript 作为单线程语言,通过事件循环机制实现了异步任务的调度与执行。整个执行过程围绕"宏任务驱动、微任务插队"展开:
-
整体 script 作为第一个宏任务执行
-
宏任务执行结束后,会立即清空所有微任务
-
微任务清空后,浏览器可能进行一次渲染
-
随后进入下一轮宏任务
这种设计保证了:
-
同步代码的顺序执行
-
异步任务的可控调度
-
Promise 状态变更的及时性
-
页面渲染的稳定性